Skip to content

Commit

Permalink
Support json-patch, and refinements to the handling of the apply link…
Browse files Browse the repository at this point in the history
…s from the chat window
  • Loading branch information
brianpos committed Mar 21, 2024
1 parent 9d4138d commit 9b4c0b6
Show file tree
Hide file tree
Showing 5 changed files with 221 additions and 4 deletions.
11 changes: 11 additions & 0 deletions components/Chat.vue
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,9 @@ export default class Chat extends Vue {
} else if (event.target?.className === "language-fhir") {
this.$emit("apply-suggested-fhir", valueString);
} else if (event.target?.className === "language-jsonpatch") {
this.$emit("apply-suggested-jsonpatch", valueString);
} else {
this.$emit("apply-suggested-json", valueString);
}
Expand Down Expand Up @@ -267,6 +270,7 @@ export default class Chat extends Vue {
.message pre:has(.language-item),
.message pre:has(.language-fhir),
.message pre:has(.language-log),
.message pre:has(.language-jsonpatch),
.message pre:has(.language-json) {
position: relative;
padding: 8px 8px 8px 16px;
Expand All @@ -282,6 +286,7 @@ export default class Chat extends Vue {
.language-questionnaire::after,
.language-item::after,
.language-fhir::after,
.language-jsonpatch::after,
.language-json::after {
content: 'assignment_return';
font-family: 'Material Icons';
Expand All @@ -301,6 +306,7 @@ export default class Chat extends Vue {
.language-questionnaire:hover::after,
.language-item:hover::after,
.language-fhir:hover::after,
.language-jsonpatch:hover::after,
.language-json:hover::after {
color: #1976d2;
}
Expand All @@ -318,6 +324,7 @@ export default class Chat extends Vue {
.language-item::before,
.language-fhir::before,
.language-log::before,
.language-jsonpatch::before,
.language-json::before {
font-size: small;
font-style: italic;
Expand Down Expand Up @@ -357,6 +364,10 @@ export default class Chat extends Vue {
content: '(json)';
}
.language-jsonpatch::before {
content: '(patch)';
}
.message pre:has(code) {
background-color: ghostwhite;
}
Expand Down
147 changes: 147 additions & 0 deletions helpers/openai_form_tester.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import {
EvaluateChatPrompt,
GetSystemPrompt,
IOpenAISettings,
} from "~/helpers/openai_utils";
import Chat from "~/components/Chat.vue";
import {
ChatMessage,
OpenAIClient,
AzureKeyCredential,
OpenAIKeyCredential,
OpenAIClientOptions,
} from "@azure/openai";
import { types } from "fhirpath";

// 1. Determine data required for Query
// (display a message)
// 2. Retrieve Data
// (code: evaluate expression to get types/known/hidden issues)
// 3. Evaluate Query
// 4. Verify results
// (code)
// 5. Display Results

export interface IDataRequired {
Mode: "edit-questionnaire" | "create-questionnaire" | "create-item" | "edit-item" | "unknown";
QuestionnaireDefinition: number; // the complete definition is required
QuestionnaireResponse: number; // a sample response is required
FocusedQuestionnaireItem: number; // the question relates to a specific item in the questionnaire
SampleSourceData: number; // the question relates to pre-populating from a resource
SampleSourceResourceType?: string; // if the resource type of sample source resource is known
ErrorMessage?: string;
}

// This is just the interface above copied into a string for use inside the OpenAI query prompt
const interfaceIDataRequired = `
interface IDataRequired {
Mode: "edit-questionnaire" | "create-questionnaire" | "create-item" | "edit-item" | "unknown";
QuestionnaireDefinition : number; // the complete definition is required as the request is editing the current form 0-10
QuestionnaireResponse : number; // a sample response is required 0-10
FocusedQuestionnaireItem : number; // the question relates to a specific item in the questionnaire 0-10
SampleSourceData: Number; // the question relates to pre-populating from a resource 0-10
ErrorMessage?: string;
}
`;

export async function DetectDataRequiredForQuery(
settings: IOpenAISettings,
query: string
): Promise<IDataRequired> {
let prompt: Array<ChatMessage> = [];

let systemPrompt = `
You are a background system assisting with verifying what data would be required to answer a provided question.
Your response is a json object that conforms to this interface:
\`\`\` typescript
${interfaceIDataRequired}
\`\`\`
What data would be required to answer the following question:
` + query;

prompt.push({ role: "system", content: systemPrompt });

let result = await EvaluateChatPrompt(prompt, settings, 1, 3096);
try {
if (result) {
console.log(result);
const parsedResponse : IDataRequired = JSON.parse(result);
console.log(parsedResponse);
return parsedResponse;
}
} catch (err) {
console.log(err);
}

return {
QuestionnaireDefinition: 1,
QuestionnaireResponse: 1,
FocusedQuestionnaireItem: 1,
SampleSourceData: 1,
SampleSourceResourceType: "Patient",
};
}

// async function handleSendMessage(message: string) {
// console.log("Message sent:", message);
// const chat = this.$refs.chatComponent as Chat;

// this.openAIexpressionExplanationLoading = true;
// // this.openAIexpressionExplanationMessage = "Asking question...";
// chat.setThinking(true);

// // before asking the question, check to see if the question would require
// // the complete questionnaire to be included
// let promptPreparationCheck: string = `
// You are a background system assisting with verifying what data would be required to answer a provided question.
// `;

// // Perform any additional actions with the message here
// const systemPrompt = GetSystemPrompt();

// let userQuestionContext: string = "";
// if (this.resourceJsonEditor) {
// var jsonValue = this.resourceJsonEditor.getValue();
// if (jsonValue.length > 0) {
// try {
// var obj = JSON.parse(jsonValue) as fhir4b.Questionnaire;
// if (obj.text) delete obj.text;
// jsonValue = JSON.stringify(obj);
// } catch (err) {
// console.log(err);
// }
// userQuestionContext += `Based on the FHIR Questionnaire\r\n\`\`\` json\r\n ${jsonValue}\n\n\`\`\`\r\n`;
// }
// }

// if (userQuestionContext != this.openAILastContext) {
// if (userQuestionContext.length > 0)
// chat.addMessage("Author", userQuestionContext, false);
// this.openAILastContext = userQuestionContext;
// }
// chat.addMessage("Author", message, true);

// // userQuestion += message;
// // chat.addMessage("Author", userQuestion, true);

// let prompt: Array<ChatMessage> = [];
// prompt.push({ role: "system", content: systemPrompt });
// prompt = prompt.concat(chat.getConversationChat());

// const resultOfQuestion = await EvaluateChatPrompt(
// prompt,
// this.GetAISettings(),
// 1,
// 4000
// );
// // this.openAIexpressionExplanationMessage = "(Generated by OpenAI " + settings.getOpenAIModel() + ")";
// this.openAIexpressionExplanationLoading = false;
// chat.addMessage(
// "FhirPath AI",
// resultOfQuestion ?? "",
// true,
// settings.getOpenAIModel()
// );
// chat.setThinking(false);
// }
11 changes: 11 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"consola": "^2.15.3",
"core-js": "^3.15.1",
"express": "^4.17.2",
"fast-json-patch": "^3.1.1",
"fhir-extension-helpers": "^0.3.0",
"fhir-sdc-helpers": "^0.1.0",
"fhirclient": "^2.5.2",
Expand Down
55 changes: 51 additions & 4 deletions pages/Questionnaire/tester.vue
Original file line number Diff line number Diff line change
Expand Up @@ -286,8 +286,12 @@
:suggestions="chatPromptOptions"
@remove-suggestion="removeSuggestion"
@reset-conversation="resetConversation"
@apply-suggested-expression="applySuggestedExpression"
@apply-suggested-questionnaire="applySuggestedExpression"
@apply-suggested-expression="copySuggestionToClipboard"
@apply-suggested-questionnaire="applySuggestedQuestionnaire"
@apply-suggested-item="applySuggestedItem"
@apply-suggested-fhir="copySuggestionToClipboard"
@apply-suggested-json="copySuggestionToClipboard"
@apply-suggested-jsonpatch="applySuggestedJsonPatch"
/>
</template>
</twin-pane-tab>
Expand Down Expand Up @@ -395,7 +399,9 @@ import {
GetSystemPrompt,
IOpenAISettings,
} from "~/helpers/openai_utils";
import { DetectDataRequiredForQuery } from "~/helpers/openai_form_tester";
import TwinPaneTab, { TabData } from "~/components/TwinPaneTab.vue";
import * as jsonpatch from 'fast-json-patch';
// import "fhirclient";
// import { FHIR } from "fhirclient";
Expand Down Expand Up @@ -1196,7 +1202,12 @@ export default Vue.extend({
}
},
applySuggestedExpression(updatedExpression: string): void {
copySuggestionToClipboard(suggestion: string) {
console.log('Copied suggestion to clipboard: ', suggestion)
navigator.clipboard.writeText(suggestion);
},
applySuggestedQuestionnaire(updatedExpression: string): void {
// before blindly applying the updated text, do some cleaning of the context
if (this.resourceJsonEditor) {
const jsonValue = this.resourceJsonEditor.getValue();
Expand All @@ -1206,7 +1217,43 @@ export default Vue.extend({
);
this.resourceJsonEditor.clearSelection();
this.resourceJsonEditor.renderer.updateFull(true);
} catch {}
} catch(err) {
console.log("Error applying json patch: ", err);
}
}
},
applySuggestedItem(updatedExpression: string): void {
// before blindly applying the updated text, do some cleaning of the context
if (this.resourceJsonEditor) {
const jsonValue = this.resourceJsonEditor.getValue();
try {
this.resourceJsonEditor.setValue(
JSON.stringify(JSON.parse(updatedExpression), null, 4)
);
this.resourceJsonEditor.clearSelection();
this.resourceJsonEditor.renderer.updateFull(true);
} catch(err) {
console.log("Error applying json patch: ", err);
}
}
},
applySuggestedJsonPatch(jsonPatchString: string){
if (this.resourceJsonEditor) {
const jsonValue = this.resourceJsonEditor.getValue();
try {
var jsonPatch = JSON.parse(jsonPatchString);
var jsonValueObj = JSON.parse(jsonValue);
jsonPatch.forEach((patch: any) => {
jsonValueObj = jsonpatch.applyPatch(jsonValueObj, [patch]).newDocument;
});
this.resourceJsonEditor.setValue(JSON.stringify(jsonValueObj, null, 4));
this.resourceJsonEditor.clearSelection();
this.resourceJsonEditor.renderer.updateFull(true);
} catch(err) {
console.log("Error applying json patch: ", err);
}
}
},
Expand Down

0 comments on commit 9b4c0b6

Please sign in to comment.