Changeset 38816 for gs3-extensions


Ignore:
Timestamp:
2024-03-02T23:03:08+13:00 (3 months ago)
Author:
davidb
Message:

A series of improvements to the code, up to having a routine that traverses the annotations that are included in the assistant reponse.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • gs3-extensions/dl-chatgpt/trunk/src/react-gui/pages/api/chat.ts

    r38809 r38816  
    1818
    1919
    20 import { MessageCreateParams, MessageContentText } from 'openai/resources/beta/threads/messages/messages'
    21 
     20import {
     21    MessageContentText,
     22    MessageCreateParams
     23} from 'openai/resources/beta/threads/messages/messages'
    2224
    2325import type { NextApiRequest, NextApiResponse } from "next";
     
    2830});
    2931
    30 const threadByUserId = new Map<string, any>();
     32let GS3AssistantLookup : any = null
     33
     34async function initGS3AssistantsLookup() {
     35//const initGS3AssistantsLookup = async () => {
     36    GS3AssistantLookup = new Map<string, any>();
     37
     38    console.log("Setting up GS3AssistantsLookup table");
     39
     40    console.log("  Retriving first page block, reverse order, so latest GS3 assistant IDs stored");
     41    // AssistantsPage
     42    //let assistants_list_cursor : CursorPage<Assistant> = await openai.beta.assistants.list({
     43    let assistants_list_cursor = await openai.beta.assistants.list({
     44    order: "asc",
     45    limit: 20,
     46    });
     47    let paginated_items = assistants_list_cursor.getPaginatedItems();
     48   
     49    //while (assistants_list_cursor !== null) {
     50    while (paginated_items.length > 0) {
     51    let last_checked_assistant_id = "";
     52   
     53    for (const openai_assistant of paginated_items) {
     54        const current_assistant_id = openai_assistant.id;
     55       
     56        const metadata : any = openai_assistant.metadata;
     57       
     58        if ("greenstone3" in metadata) {
     59        if (metadata.greenstone3 === "True") {
     60            console.log("    Processing Greenstone3 Assistant:");
     61            //console.log(openai_assistant);
     62           
     63            const site_name = metadata.siteName;
     64            const coll_name = metadata.collectionName;
     65
     66            //console.log(`    Adding entry for site: ${site_name}, collect: ${coll_name}`);
     67           
     68            if (!(site_name in GS3AssistantLookup)) {
     69            GS3AssistantLookup[site_name] = new Map<string, any>();
     70            }
     71            else {
     72            // site already setup and going ....
     73            // check to see if pre-existign coll_name already there?
     74            if (coll_name in GS3AssistantLookup[site_name]) {
     75                console.error(`Warning: Overwriting pre-existing GS3 assistant for site: ${site_name}, collection: ${coll_name}`);
     76            }
     77            }
     78           
     79            console.log(`    Storing GS3AssistantsLook[${site_name}][${coll_name}]: ${current_assistant_id}`);
     80            GS3AssistantLookup[site_name][coll_name] = current_assistant_id;
     81
     82        }       
     83        }
     84        last_checked_assistant_id = current_assistant_id;
     85    }
     86
     87   
     88    console.log("  Retrieving next page block, ordered by most recent assistants");
     89    assistants_list_cursor = await openai.beta.assistants.list({
     90        after: last_checked_assistant_id,
     91        order: "asc",
     92        limit: 20,
     93    });
     94    paginated_items = assistants_list_cursor.getPaginatedItems();
     95    }
     96    console.log("Finished setting up GS3AssistantsLookup table");
     97}
     98
     99/*
     100
     101function processAnnotations(message_content)
     102{
     103   
     104    annotations = message_content.annotations;
     105
     106    citations = [];
     107
     108    // Iterate over the annotations and add footnotes
     109    for index, annotation in enumerate(annotations) {
     110    //Replace the text with a footnote
     111    message_content.value = message_content.value.replace(annotation.text, f' [{index}]')l
     112
     113    // Gather citations based on annotation attributes
     114    if (file_citation := getattr(annotation, 'file_citation', None)) {
     115            cited_file = client.files.retrieve(file_citation.file_id)
     116            citations.append(f'[{index}] {file_citation.quote} from {cited_file.filename}')
     117    }
     118    elif (file_path := getattr(annotation, 'file_path', None)) {
     119            cited_file = client.files.retrieve(file_path.file_id)
     120            citations.append(f'[{index}] Click <here> to download {cited_file.filename}')
     121            // Note: File download functionality not implemented above for brevity
     122    }
     123    }
     124   
     125    //Add footnotes to the end of the message before displaying to user
     126    message_content.value += '\n' + '\n'.join(citations)
     127}
     128*/
     129
     130
     131
     132function processAnnotations(message : MessageContentText)
     133{
     134    console.log("Process Annotations");
     135   
     136    const messageContent = message.text;
     137    const annotations    = messageContent.annotations;
     138
     139    const citations: string[] = [];
     140   
     141    // Iterate over the annotations and add footnotes
     142    annotations.forEach(async (annotation, index) => {
     143    // Replace the text with a footnote
     144    console.log(`  Annotation[${index}: `, annotation);
     145
     146    messageContent.value = messageContent.value.replace(annotation.text, ` [${index}]`);
     147   
     148    // Gather citations based on annotation attributes
     149    if ("file_citation" in annotation) {
     150        const file_citation = annotation.file_citation;
     151        console.log(`    Retrieving file_citation for ${file_citation.file_id}`);
     152        const citedFile = await openai.files.retrieve(file_citation.file_id);
     153        const quote_html = file_citation.quote;
     154        const quote = quote_html.replace(/(<([^>]+)>)/g," ").replace(/(\\n|\n)/," ").replace(/\s+/g," ")
     155        console.log(`    quote = ${quote}`);
     156       
     157        citations.push(`[${index}] ${file_citation.quote} from ${citedFile.filename}`);
     158    }
     159    else if ("file_path" in annotation) {
     160        const file_path = annotation.file_path;
     161        console.log(`    Retrieving file_path for ${annotation.file_path.file_id}`);
     162        const citedFile = await openai.files.retrieve(annotation.file_path.file_id);
     163        citations.push(`[${index}] Click <here> to download ${citedFile.filename}`);
     164        // Note: File download functionality not implemented above for brevity
     165    }
     166    });
     167   
     168    // Add footnotes to the end of the message before displaying to the user
     169    console.log("Citations: ", citations, '\n');
     170    //console.log("Appending: " + citations.join('\n'));
     171       
     172    messageContent.value += '\n' + citations.join('\n');
     173
     174    //return messageContent.value;
     175}
     176
     177
     178   
     179
     180   
     181const UserThreadIdLookup = new Map<string, any>();
    31182
    32183async function assistantHandler(
     
    34185    res: NextApiResponse
    35186) {   
    36     const assistantIdToUse = process.env.ASSISTANT_ID || "";
    37     // const assistantIdToUse = "asst_LwUi7f2YMkZG5qTIPtpyhCOD";
     187    if (GS3AssistantLookup === null) {
     188    await initGS3AssistantsLookup();
     189    }
     190
     191
     192    // const assistantIdToUse = process.env.ASSISTANT_ID || "";
     193    // // const assistantIdToUse = "asst_LwUi7f2YMkZG5qTIPtpyhCOD";
     194
     195    const assistantIdToUse = GS3AssistantLookup["thewillow"]["community-contributions"];
    38196   
    39197    //const userId = req.body.userId; // You should include the user ID in the request
    40198    const userId = "test_username";
     199
    41200   
    42201    // Create a new thread if it's the user's first message
    43     //if (!threadByUserId[userId]) {
    44     if (!threadByUserId.has(userId)) {
     202    //if (!UserThreadIdLookup[userId]) {
     203    if (!UserThreadIdLookup.has(userId)) {
    45204    try {
    46205        const myThread = await openai.beta.threads.create();
     
    48207        console.log(`Created new thread with ID: '${myThread.id}'"`);
    49208        console.log("========")
    50         //threadByUserId[userId] = myThread.id; // Store the thread ID for this user
    51        
    52         threadByUserId.set(userId,myThread.id); // Store the thread ID for this user
     209        //UserThreadIdLookup[userId] = myThread.id; // Store the thread ID for this user
     210       
     211        UserThreadIdLookup.set(userId,myThread.id); // Store the thread ID for this user
    53212    }
    54213    catch (error) {
     
    73232    try {
    74233    console.log("  Retrieving thread for userId: " + userId);
    75     const myThreadId = threadByUserId.get(userId);
     234    const myThreadId = UserThreadIdLookup.get(userId);
    76235    console.log("  myThreadId = " + myThreadId);
    77236    console.log("");
     
    84243    const myThreadMessage = await openai.beta.threads.messages.create(myThreadId,user_message);
    85244
    86     console.log("This is the message object: ", myThreadMessage, "\n");
     245    //console.log("This is the message object: ", myThreadMessage, "\n");
    87246   
    88247    // Run the Assistant
    89248    console.log("Starting run for threadId: " + myThreadId);
    90249    const myRun = await openai.beta.threads.runs.create(
    91         //threadByUserId.get(userId),
     250        //UserThreadIdLookup.get(userId),
    92251        myThreadId,
    93252        {
     
    100259        }
    101260    );
    102     console.log("This is the run object: ", myRun, "\n");
     261    //console.log("This is the run object: ", myRun, "\n");
     262    console.log("myRun: ", myRun, "\n");
     263
     264    const checkStatusUntilCompleted = async (thread_id: string, run_id: string) => {
     265        // Give a run that's just started some time to get going
     266        await new Promise((resolve) => setTimeout(resolve, 5000));     
     267        let run = await openai.beta.threads.runs.retrieve(thread_id,run_id);
     268
     269        let run_loop_count = 0;
     270        while ((run.status === "queued") || (run.status === "in_progress")) {
     271        run_loop_count++;
     272       
     273        await new Promise((resolve) => setTimeout(resolve, 1000));
     274        console.log(`  ${thread_id} run_id ${run_id}: Getting update on progress [ ` + "=".repeat(run_loop_count)+"> ]");
     275        run = await openai.beta.threads.runs.retrieve(thread_id,run_id);
     276        console.log(`  ${thread_id} run_id ${run_id}: Status = ${run.status}`);
     277       
     278        // The following is not strictly necessary, but nice to have logged
     279        if (run.status === "completed") {
     280            console.log(`  ${thread_id} run_id ${run_id}: Completed!`);
     281            console.log("--------");
     282            break;
     283        }
     284       
     285        }
     286    }
    103287   
    104288    // Periodically retrieve the Run to check on its status
     
    112296       
    113297        while (myRun.status !== "completed") {
    114         console.log(`    ${rrId}: Away to do runs.retrieve() when myRun id ${myRun.id} status is ${myRun.status}`);
     298        //console.log(`    ${rrId}: Away to do runs.retrieve() when myRun id ${myRun.id} status is ${myRun.status}`);
    115299        keepRetrievingRun = await openai.beta.threads.runs.retrieve(
    116             //threadByUserId.get(userId), // Use the stored thread ID for this user
     300            //UserThreadIdLookup.get(userId), // Use the stored thread ID for this user
    117301            myThreadId,
    118302            myRun.id
     
    134318    // Retrieve the Messages added by the Assistant to the Thread
    135319    const waitForAssistantMessage = async () => {
    136         console.log("waitForAssistantMessage() away to call await retrieveRun()");
    137         await retrieveRun("withinWait");
    138         console.log("waitForAssistantMessage() returned from retrieveRun()");
     320        console.log("waitForAssistantMessage() away to call await checkStatusUntilComplete()");
     321        //await retrieveRun("withinWait");
     322        await checkStatusUntilCompleted(myThreadId,myRun.id);
     323        console.log("waitForAssistantMessage() returned from checkRunUntilComplete()");
    139324       
    140325        console.log(`Retrieving messages list for ${myThreadId}`);
    141326        const allMessages = await openai.beta.threads.messages.list(
    142         //threadByUserId.get(userId) // Use the stored thread ID for this user
     327        //UserThreadIdLookup.get(userId) // Use the stored thread ID for this user
    143328        myThreadId
    144329        );
    145330       
    146         // Send the response back to the front end
    147         /*
    148         res.status(200).json({
    149         response: allMessages.data[0].content[0].text.value,
    150         });
    151         */
    152331        console.log(
    153         "------------------------------------------------------------ \n"
     332        "--------\n"
    154333        );
    155334
    156335        const user_request_message: MessageContentText = (myThreadMessage.content[0]) as MessageContentText;
    157         const assistant_response_message = allMessages.data[0].content[0] as MessageContentText;
    158    
     336        const assistant_response_message = (allMessages.data[0].content[0]) as MessageContentText;
     337
     338        console.log("#####")
     339        console.log("Assistant response message:", assistant_response_message, "\n");
     340        console.log("#####")
     341
     342        processAnnotations(assistant_response_message);
     343       
    159344        console.log("User: ", user_request_message.text.value);
    160345        console.log("Assistant: ", assistant_response_message.text.value);
Note: See TracChangeset for help on using the changeset viewer.