Skip to content

Commit

Permalink
MyDataHelps AIAssistant (#285)
Browse files Browse the repository at this point in the history
* Using the corresponding mdh.js package.

* Added mdh assistant component.

* 2.24.3-mdhai.0

* Package.json updates?

* 2.24.3-mdhai.1

* Bug fix.

* 2.24.3-mdhai.2

* Bug fix.

* 2.24.3-mdhai.3

* Bug fixes.

* 2.24.3-mdhai.4

* Bug fixes.

* 2.24.3-mdhai.5

* Bug fix.

* 2.24.3-mdhai.6

* Debug.

* 2.24.3-mdhai.7

* More debug.

* 2.24.3-mdhai.8

* Bug fixes.

* 2.24.3-mdhai.9

* Adding some styles.

* 2.24.3-mdhai.10

* UI updates.

* 2.24.3-mdhai.11

* More UI tweaks.

* 2.24.3-mdhai.12

* Using MyDataHelps.js 3.18.0.

* Readding the MyDataHelps assistant tools.

* Added assistant code.

* 2.24.3-MdhAssistantComponent.0

* Upping size limit.

* 2.24.3-MdhAssistantComponent.1

* Minor bug fix.

* 2.24.3-MdhAssistantComponent.2

* Debug.

* 2.24.3-MdhAssistantComponent.3

* Streaming updates.

* 2.24.3-MdhAssistantComponent.4

* Bug fix.

* 2.24.3-MdhAssistantComponent.5

* Debug.

* 2.24.3-MdhAssistantComponent.6

* Bug fix.

* 2.24.3-MdhAssistantComponent.7

* Using a ref to the assistant so that it doesn't lose state.

* 2.24.3-MdhAssistantComponent.8

* Minor bug fix. Removing mermaid instructions temporarily.

* 2.24.3-MdhAssistantComponent.9

* Adding react-markdown.

* 2.24.3-MdhAssistantComponent.10

* Bug fix.

* 2.24.3-MdhAssistantComponent.11

* UI tweaks.

* 2.24.3-MdhAssistantComponent.12

* UI tweaks.

* 2.24.3-MdhAssistantComponent.13

* UI tweaks.

* 2.24.3-MdhAssistantComponent.14

* UI tweaks.

* 2.24.3-MdhAssistantComponent.15

* Added scroll to bottom.

* 2.24.3-MdhAssistantComponent.16

* Debug.

* 2.24.3-MdhAssistantComponent.17

* Bug fix.

* 2.24.3-MdhAssistantComponent.18

* UI tweak.

* Added mermaid.

* 2.24.3-MdhAssistantComponent.19

* Rollup tweaks.

* 2.24.3-MdhAssistantComponent.20

* Tweak.

* 2.24.3-MdhAssistantComponent.21

* Debug.

* 2.24.3-MdhAssistantComponent.22

* Tweaks.

* 2.24.3-MdhAssistantComponent.23

* Tweak.

* 2.24.3-MdhAssistantComponent.24

* debug

* 2.24.3-MdhAssistantComponent.25

* Bug fix.

* 2.24.3-MdhAssistantComponent.26

* Tweak.

* 2.24.3-MdhAssistantComponent.27

* Debug.

* 2.24.3-MdhAssistantComponent.28

* UI polishes.

* 2.24.3-MdhAssistantComponent.29

* More UI tweaks.

* 2.24.3-MdhAssistantComponent.30

* UI changes.

* 2.24.3-MdhAssistantComponent.31

* Latest.

* 2.24.3-MdhAssistantComponent.32

* Latest.

* 2.24.3-MdhAssistantComponent.33

* Few final UI tweaks.

* 2.24.3-MdhAssistantComponent.34

* Removing some debug.

* 2.24.3-MdhAssistantComponent.35

* Removing mermaid.

* 2.24.3-MdhAssistantComponent.36

* Debug.

* 2.24.3-MdhAssistantComponent.37

* Tweaks.

* 2.24.3-MdhAssistantComponent.38

* Adding some error handling.

* 2.24.3-MdhAssistantComponent.39

* Latest packages.

* Minor tweak.

* 2.24.3-MdhAssistantComponent.40

* Tweak.

* 2.24.3-MdhAssistantComponent.41

* Adding a query daily data tool.

* 2.24.3-MdhAssistantComponent.42

* Bug fixes.

* Tweak.

* 2.24.3-MdhAssistantComponent.43

* Added getAllDailyDataTypes tool.

* 2.24.3-MdhAssistantComponent.44

* Tweak to daily data tool.

* 2.24.3-MdhAssistantComponent.45

* Added ability to pass in additional tools.

* Added a couple of exports.

* 2.24.3-MdhAssistantComponent.46

* Added storybook for MyDataHelps Assistant.

* Extracted some things into a Chat presentational component.

* Made new AIAssistant container component use the new Chat presentational component.

* Few more renames.

* Added popup presentational widget.

* Added stories for Chat.

* Added Popup story.

* Misc UI tweaks and polishes.

* Misc tweaks and updates.

* Add ability to query EHR data.

* Tweaks to instructions.

* Updated mydatahelps js to 3.20.0.

* Minor bug fix.

* Bug fixes.

* Reenabling sourcemaps.

* 2.24.3-MdhAssistantComponent.47

* Minor UI tweak.

* Adding some paging instructions.

* Minor tweaks.

* Refactoring.

* Tweak.

* Changes from code review.

* Tweaks.

* Various UI tweaks.

* More UI and layout tweaks.

* 2.24.3-MdhAssistantComponent.48

* Merge cleanup.

* 2.28.1-MdhAssistantComponent.0

* Minor layout tweak.

* 2.28.1-MdhAssistantComponent.1

* Debug.

* 2.28.1-MdhAssistantComponent.2

* Adding layout resize handler.

* 2.28.1-MdhAssistantComponent.3

* More debug.

* 2.28.1-MdhAssistantComponent.4

* Tweak.

* 2.28.1-MdhAssistantComponent.5

* Removing debug.

* 2.28.1-MdhAssistantComponent.6

* Trying a flex layout.

* 2.28.1-MdhAssistantComponent.7

* Adding some height: 100%.

* 2.28.1-MdhAssistantComponent.8

* Layout tweaks.

* 2.28.1-MdhAssistantComponent.9

* Layout tweaks.

* 2.28.1-MdhAssistantComponent.10

* Layout tweaks.

* 2.28.1-MdhAssistantComponent.11

* More tweaks.

* 2.28.1-MdhAssistantComponent.12

* Tweak.

* 2.28.1-MdhAssistantComponent.13

* Tweaks and bug fixes. Removing popup.

* 2.28.1-MdhAssistantComponent.14

* Added event tracking for AI Assistant messages.

* Minor tweak.

* Made baseUrl configurable.

* Added theme support to Chat.

* Changes from code review.

* Added project info to AIAssistant base prompt.

* Added a localized string for ai assistant loading.

* Assistant icon tweak.

* Tweaks.

* No longer sending user input if there is no message or if the ai is currently responding.

* Showing messages from the bottom of the chat window.

* Reverting package versions.
  • Loading branch information
vladtar committed Sep 1, 2024
1 parent 73d0bc1 commit fec940c
Show file tree
Hide file tree
Showing 28 changed files with 1,391 additions and 43 deletions.
360 changes: 335 additions & 25 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@
"@fortawesome/free-regular-svg-icons": "^6.4.2",
"@fortawesome/free-solid-svg-icons": "^6.4.2",
"@fortawesome/react-fontawesome": "^0.2.0",
"@langchain/core": "^0.2.18",
"@langchain/langgraph": "^0.0.31",
"@langchain/openai": "^0.2.5",
"@types/lodash": "^4.14.195",
"date-fns": "^2.28.0",
"html-react-parser": "^5.0.7",
Expand All @@ -72,6 +75,7 @@
"react-feather": "^2.0.9",
"react-fontawesome-svg-icon": "^1.1.2",
"recharts": "^2.9.0",
"zod": "^3.23.8",
"uuid": "^10.0.0"
},
"peerDependencies": {
Expand Down
8 changes: 4 additions & 4 deletions rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { terser } from "rollup-plugin-terser";
import peerDepsExternal from 'rollup-plugin-peer-deps-external';
import analyze from 'rollup-plugin-analyzer';

const limitBytes = 5e6
const limitBytes = 6e6;

const onAnalysis = ({ bundleSize }) => {
if (bundleSize < limitBytes) return
Expand All @@ -25,19 +25,19 @@ export default [
{
file: packageJson.main,
format: "cjs",
sourcemap: true,
sourcemap: true
},
{
file: packageJson.module,
format: "esm",
sourcemap: true,
sourcemap: true
},
],
plugins: [
peerDepsExternal(),
resolve(),
commonjs(),
typescript({ tsconfig: "./tsconfig.json", sourceMap: false }),
typescript({ tsconfig: "./tsconfig.json", sourceMap: true }),
postcss(),
terser(),
image(),
Expand Down
101 changes: 101 additions & 0 deletions src/components/container/AIAssistant/AIAssistant.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import React from "react";
import { Global, css } from '@emotion/react';
import Layout from "../../presentational/Layout";
import AIAssistant, { AIAssistantProps } from "./AIAssistant";
import { MyDataHelpsTools } from "../../../helpers/AIAssistant";

export default {
title: 'Container/AIAssistant',
component: AIAssistant,
parameters: { layout: 'fullscreen' },
argTypes: {
colorScheme: {
name: 'color scheme',
control: 'radio',
options: ['auto', 'light', 'dark']
},
debug: {
control: 'boolean',
description: 'If turned on the assistant prints what tools it is trying to call and the corresponding parameters.',
defaultValue: {
summary: false
}
},
additionalInstructions: {
control: 'text',
description: 'Additional system instructions to be passed to the assistant.',
defaultValue: {
summary: ""
}
},
appendTools: {
control: false,
description: 'If turned on the tools passed in the tools prop will be appended to the default tools.',
defaultValue: {
summary: true
}
},
tools: {
control: 'multi-select',
options: ['persistParticipantInfo', 'queryAppleHealthActivitySummaries', 'queryAppleHealthWorkouts', 'getAllDailyDataTypes',
'queryDailySleep', 'queryDeviceDataV2Aggregate', 'queryDeviceDataV2', 'queryNotifications',
'querySurveyAnswers', 'queryDailyData', 'getAllDailyDataTypes', 'getEhrNewsFeedPage'],
mapping: {
persistParticipantInfo: new MyDataHelpsTools.PersistParticipantInfoTool(),
queryAppleHealthActivitySummaries: new MyDataHelpsTools.QueryAppleHealthActivitySummariesTool(),
queryAppleHealthWorkouts: new MyDataHelpsTools.QueryAppleHealthWorkoutsTool(),
queryDailySleep: new MyDataHelpsTools.QueryDailySleepTool(),
queryDeviceDataV2Aggregate: new MyDataHelpsTools.QueryDeviceDataV2AggregateTool(),
queryDeviceDataV2: new MyDataHelpsTools.QueryDeviceDataV2Tool(),
queryNotifications: new MyDataHelpsTools.QueryNotificationsTool(),
querySurveyAnswers: new MyDataHelpsTools.QuerySurveyAnswersTool(),
queryDailyDataTool: new MyDataHelpsTools.QueryDailyDataTool(),
getAllDailyDataTypes: new MyDataHelpsTools.GetAllDailyDataTypesTool(),
getEhrNewsFeedPage: new MyDataHelpsTools.GetEhrNewsFeedPageTool(),
getDeviceDataV2AllDataTypes: new MyDataHelpsTools.GetDeviceDataV2AllDataTypesTool()
}
}
}
};

interface AIAssistantStoryArgs extends AIAssistantProps {
colorScheme: 'auto' | 'light' | 'dark';
}

const render = (args: AIAssistantStoryArgs) => {
return <Layout colorScheme={args.colorScheme}>
<Global styles={css`
html {
height: 100%;
}
body {
height: 100%;
}
#storybook-root {
height: 100%;
}
.mdhui-layout {
height: 100%;
}
`} />
<AIAssistant {...args} appendTools={false} />
</Layout>
};

export const Default = {
args: {
colorScheme: "auto",
debug: false
},
render: render
};

export const Debug = {
args: {
debug: true
},
render: render
};
135 changes: 135 additions & 0 deletions src/components/container/AIAssistant/AIAssistant.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import React, { useEffect, useRef, useState } from 'react';
import { FontAwesomeSvgIcon } from 'react-fontawesome-svg-icon';
import { faLightbulb } from '@fortawesome/free-regular-svg-icons/faLightbulb';
import { StreamEvent } from '@langchain/core/tracers/log_stream';
import { AIMessageChunk } from '@langchain/core/messages';
import { StructuredTool } from '@langchain/core/tools';
import MyDataHelps from '@careevolution/mydatahelps-js';

import { MyDataHelpsAIAssistant } from '../../../helpers/AIAssistant/AIAssistant';
import language from '../../../helpers/language';
import Chat from '../../presentational/Chat';

import '@fortawesome/fontawesome-svg-core/styles.css';

export interface AIAssistantProps {
innerRef?: React.Ref<HTMLDivElement>;
previewState?: "default";
debug: boolean;
additionalInstructions?: string;
tools?: StructuredTool[];
appendTools?: boolean;
baseUrl?: string;
}

export type AIAssistantMessageType = "user" | "ai";

export interface AIAssistantMessage {
type: AIAssistantMessageType;
content: string;
runId?: string;
}

export default function (props: AIAssistantProps) {

const [messages, setMessages] = useState<AIAssistantMessage[]>([]);
const [loading, setLoading] = useState("");
const [inputDisabled, setInputDisabled] = useState(false);

let lastAIMessage = "";

const assistantRef = useRef<MyDataHelpsAIAssistant>();

useEffect(() => {
if (assistantRef.current === undefined) {
assistantRef.current = new MyDataHelpsAIAssistant(props.baseUrl, props.additionalInstructions, props.tools, props.appendTools);
}
}, []);

const addUserMessage = async function (newMessage: string) {

setMessages(prevMessages => [...prevMessages, { type: 'user', content: newMessage }]);
setInputDisabled(true);

MyDataHelps.trackCustomEvent({
eventType: "ai-assistant-message",
properties: {
type: "user",
body: newMessage
}
});

await assistantRef.current?.ask(newMessage, function (streamEvent: StreamEvent) {

const [kind, type] = streamEvent.event.split("_").slice(1);

if (type === "stream" && kind !== "chain") {
const chunk = streamEvent.data?.chunk;
let msg = chunk.message as AIMessageChunk;

if (msg.content && typeof msg.content === "string") {
addMessageChunk(streamEvent.run_id, msg.content);
}
else if (props.debug && msg.tool_call_chunks && msg.tool_call_chunks.length > 0) {
if (msg.tool_call_chunks[0].args) {
addMessageChunk(streamEvent.run_id, msg.tool_call_chunks[0].args);
}
else if (msg.tool_call_chunks[0].name) {
addMessageChunk(streamEvent.run_id, msg.tool_call_chunks[0].name + " ");
}
}
}

if (kind === "tool") {
if (type === "start") {
setLoading(language('ai-assistant-loading'));
}
else if (type === "end") {
setLoading("");
}
}

if (kind === "llm" && type === "start") {
lastAIMessage = "";
}

if (kind === "llm" && type === "end") {

MyDataHelps.trackCustomEvent({
eventType: "ai-assistant-message",
properties: {
type: "ai",
body: lastAIMessage
}
});

setInputDisabled(false);
}
});
}

const addMessageChunk = function (runId: string, message: string) {
setMessages(prevMessages => {
let existingMessage = prevMessages.find((msg) => msg.runId === runId);
if (existingMessage) {
const updatedMessage = { ...existingMessage, content: existingMessage.content + message };
return prevMessages.map(msg => msg.runId === runId ? updatedMessage : msg);
}
else {
return [...prevMessages, { type: 'ai', content: message, runId: runId }];
}
});

lastAIMessage += message;
}

return <>
{messages && <Chat innerRef={props.innerRef} messages={messages.map((msg) => {
return {
icon: msg.type === "ai" ? <FontAwesomeSvgIcon icon={faLightbulb} width={16} /> : undefined,
content: msg.content,
type: msg.type === "user" ? "sent" : "received"
}
})} onSendMessage={addUserMessage} loading={loading} inputDisabled={inputDisabled} />}
</>
}
1 change: 1 addition & 0 deletions src/components/container/AIAssistant/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from "./AIAssistant";
1 change: 1 addition & 0 deletions src/components/container/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,4 @@ export { default as TermInformation, TermInformationReference } from "./TermInfo
export { default as ViewEhr } from "./ViewEhr"
export { default as InboxItemList } from "./InboxItemList"
export { default as InboxItemListCoordinator } from "./InboxItemListCoordinator"
export { default as AIAssistant } from "./AIAssistant"
Loading

0 comments on commit fec940c

Please sign in to comment.