Integrating / embedding chat GPT for multiple turns | XM Community
Skip to main content

Integrating / embedding chat GPT for multiple turns

  • February 27, 2024
  • 2 replies
  • 1777 views

Forum|alt.badge.img+1

I’ve seen a bunch of people asking about how to embed chat GPT into a qualtrics survey. I managed to construct a working version of a multi-turn interaction with chatGPT, and so I’m posting the code here. Hopefully it helps someone.

The basic idea is to dynamically alter properties in the webpage, including adding custom buttons and changing the question text. You can see a demo of the survey here. Because of the way that it directly alters various webpage properties, I’m quite sure that it will break easily. For example, I don’t know what happens if you have multiple questions on the same page. There be dragons there.

The results of the conversation get stored in an embedded field.

I just created a single question with a text area answer, and then dropped this code into the question’s “addOnLoad” method.
 

1// this question will drop a JSON version of the conversation into this embedded data location
2const EMBEDDED_DATA_DEST = "convo_history";
3
4// adjust as needed
5const OPENAI_API_KEY = "Bearer XXX-PUT-YOUR-API-KEY-HERE-XXX";
6const OPENAI_ENDPOINT = "https://api.openai.com/v1/chat/completions";
7const OPENAI_MODEL = 'gpt-4';
8
9// after this many turns, the conversation will be disabled and the respondent will be able to advance to the next question
10const MAX_TURNS = 6;
11
12//
13// ----------------------------------------------------------------
14//
15
16// these are all used to construct the initial set of messages
17
18const GPT4_SYS_PROMPT = `You are an expert in helping individuals cultivate gratitude in their lives. You are acting as a therapist. Your goal is help the user feel more grateful in life. You have been trained in the latest academic literature about gratitude. You know that when asked what they are grateful for, most people will respond with a list of things. However, you also know that when people reflect on relationships the benefits are much greater. Therefore, you must guide the user to reflect on relationships that are important, that have shaped them, and that have shaped how they treat others.
19
20You have three specific goals:
21
22First, you must get them to identify a benevolent relationship that has had meaningful impact on their live. Ideally, this relationship would involve some sort of sacrifice on the part of the giver.
23
24Second, you must get them to reflect their feelings about that relationship and its impact.
25
26Third, you must get them to express how this relationship will change their behavior. In other words, how will this relationship drive some sort of outward expression? How will they "pay it forward"?`;
27const GPT4_INITIAL_TURN = "Hello! Thank you for meeting with me today. Could we start by having you tell me a bit about yourself?"
28const GPT4_SECOND_TURN = "Perfect. Now, can you please list three things that you are grateful for?"
29
30// grabbing some data from another question
31const user_demographics = "${e://Field/description}";
32
33//
34// ----------------------------------------------------------------
35//
36
37// the initial converation
38
39var messages = [{role: 'system', content: GPT4_SYS_PROMPT},
40        {role: 'assistant', content: GPT4_INITIAL_TURN},
41                {role: 'user', content: user_demographics},
42                {role: 'assistant', content: GPT4_SECOND_TURN}
43           ];
44Qualtrics.SurveyEngine.setEmbeddedData( EMBEDDED_DATA_DEST , JSON.stringify( messages ) );
45
46//
47// ----------------------------------------------------------------
48// ----------------------------------------------------------------
49// ----------------------------------------------------------------
50//
51
52// store this reference for future use
53const question_this = this;
54
55var initial_messages_length = messages.length;  // check this later
56
57this.disableNextButton();
58
59// I had no luck hiding the "done" button, so I'm just deleting it
60jQuery(".advanceButtonContainer").empty();
61
62// create my own button
63jQuery("<div style='display:inline-block;padding:20 20 20 20;'><input type='button' value='Submit response' class='AdvanceButton Button' id='submitButton' disabled /></div>").appendTo( jQuery(".QuestionOuter") );
64jQuery("#submitButton").prop("disabled",false);
65
66jQuery("#submitButton").click( function() {
67
68    /*
69Things that should happen when they click the button:
70
711) Check that their input was not empty
722) Disable the submit button
733) Construct the new set of messages
744) Update the global conversation structure
755) Store a json version of the GCS
766) Ping the GPT API
777) Once we have a response, clear the question text and replace it by the GPT respone
788) clear the input text area
799) re-enable the submit button
8010) check to see if it's time for the convo to end / enable the "next" button
81*/
82
83    var current_user_response = jQuery(".InputText").val();
84
85    if ( ( current_user_response == undefined) || ( current_user_response.length == 0 ) ) {
86    alert("Please enter a response");
87    return;
88    }
89
90    jQuery("#submitButton").prop("disabled",true);
91
92    messages.push( {
93    role: 'user',
94    content: current_user_response,
95    });
96    Qualtrics.SurveyEngine.setEmbeddedData( EMBEDDED_DATA_DEST , JSON.stringify( messages ) );
97
98    const data = {
99        model: OPENAI_MODEL,
100        messages: messages,
101        max_tokens: 1000,
102        temperature: 1.0,
103    };
104
105    console.log( "initiating gpt query..." );
106    console.log( messages );
107
108    fetch( OPENAI_ENDPOINT, {
109    method: 'POST',
110    headers: {
111        'Content-Type': 'application/json',
112        'Authorization': OPENAI_API_KEY
113    },
114    body: JSON.stringify(data)
115    })
116    .then(response => {
117    if (!response.ok) {
118        throw new Error('Network response was not ok');
119    }
120    return response.json();
121    })
122    .then(data => {
123
124    //
125    // GOT A RESPONSE!
126    //
127
128    console.log('API Response:', data);
129
130    message = data.choices[0].message.content;
131    console.log("Message: ", message);
132
133    $$('.QuestionText')[0].innerText = message;
134
135    messages.push( {
136        role: 'assistant',
137        content: message
138    });
139    Qualtrics.SurveyEngine.setEmbeddedData( EMBEDDED_DATA_DEST , JSON.stringify( messages ) );
140
141    jQuery(".InputText").val( '' ); // reset answer field for next question
142    jQuery("#submitButton").prop("disabled",false);
143
144    // CHECK TO SEE IF THE CONVO HAS ENDED
145    if (( messages.length - initial_messages_length ) / 2 >= MAX_TURNS) {
146
147        question_this.enableNextButton();
148        jQuery("#submitButton").prop("disabled",true);
149        jQuery(".InputText").val( '' );
150        $$('.QuestionText')[0].innerText = "Thank you for your answers. Please click the 'Next' button below";
151
152    }
153
154    })
155    .catch(error => {
156    console.error('There was a problem with the fetch operation:', error);
157
158    Qualtrics.SurveyEngine.setEmbeddedData("API Error" , "true");
159
160    question_this.enableNextButton();
161    });
162
163});
164

 

2 replies

hokie404
Level 2 ●●
Forum|alt.badge.img+2
  • Level 2 ●●
  • 8 replies
  • April 29, 2024

Thanks so much for sharing, this is great! Will definitely be exploring how to use this and use the stored output.


Nanditha MM
Level 4 ●●●●
Forum|alt.badge.img+17
  • Level 4 ●●●●
  • 114 replies
  • April 29, 2024

Hi @wingated,

 

Thanks so much for sharing with us.


Leave a Reply