@worktango/ai-assistant 0.0.21 → 0.0.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -2
- package/dist/{Absenteeism-CedNvSxC.mjs → Absenteeism-BlfJuY7Q.mjs} +1 -1
- package/dist/{ActivityFeed-wI9wBx6H.mjs → ActivityFeed-CdbbvPCr.mjs} +1 -1
- package/dist/{AddUser-DE-2WrlL.mjs → AddUser-Dm5tN1Cr.mjs} +1 -1
- package/dist/{Admin-Cfl1AW_P.mjs → Admin-Cv5LVBbB.mjs} +1 -1
- package/dist/{Admin-C15wLDAT.mjs → Admin-GJZ14xCg.mjs} +1 -1
- package/dist/{Anniversary-CsvnVDOS.mjs → Anniversary-CxsqeD8j.mjs} +1 -1
- package/dist/{Appreciation-CYMmLsU_.mjs → Appreciation-CplBRYpa.mjs} +1 -1
- package/dist/{ArrowDown-Dubu_Ec7.mjs → ArrowDown-DCYs9jbi.mjs} +1 -1
- package/dist/{ArrowLeft-A1yp5aqr.mjs → ArrowLeft-DlFDpsqu.mjs} +1 -1
- package/dist/{ArrowRight-BhAh3I-x.mjs → ArrowRight-CDqrZvCr.mjs} +1 -1
- package/dist/{ArrowUp-bVYNtMgx.mjs → ArrowUp-DtWnNyRL.mjs} +1 -1
- package/dist/{Attachment-CWcY2CqV.mjs → Attachment-DEzcZHI7.mjs} +1 -1
- package/dist/{BeQuickDontHurry-DbSFImq2.mjs → BeQuickDontHurry-DoCKS3D0.mjs} +1 -1
- package/dist/{Birthday-C5IrWOxl.mjs → Birthday-COe-42mW.mjs} +1 -1
- package/dist/{BrandPromise-C_LmWwe6.mjs → BrandPromise-DT7BpKTq.mjs} +1 -1
- package/dist/{BrandVision-DYq6uUoU.mjs → BrandVision-CsF5PHr9.mjs} +1 -1
- package/dist/{Calendar-BYVlbm4s.mjs → Calendar-CbVaJVcV.mjs} +1 -1
- package/dist/{CaretDn-BeyN8HcH.mjs → CaretDn-DfXkDzAH.mjs} +1 -1
- package/dist/{CaretDown-BS3WGRra.mjs → CaretDown-DKwSpoJj.mjs} +1 -1
- package/dist/{CaretLeft-BXRjchoM.mjs → CaretLeft-DjIuIw4E.mjs} +1 -1
- package/dist/{CaretRight-CTegVEEE.mjs → CaretRight-Blv3CCWa.mjs} +1 -1
- package/dist/{CaretUp-B4tN3uVW.mjs → CaretUp-CEfq2pon.mjs} +1 -1
- package/dist/{ChallengeAccepted-DzEX4vek.mjs → ChallengeAccepted-BrB08i_g.mjs} +1 -1
- package/dist/{CheckIn-BsEJXvpg.mjs → CheckIn-DHloFCTi.mjs} +1 -1
- package/dist/{CheckIns-CeDSmtsH.mjs → CheckIns-BokF9D3N.mjs} +1 -1
- package/dist/{CheckSeal-BPPpnqgj.mjs → CheckSeal-3RBoC7kt.mjs} +1 -1
- package/dist/{Checkmark-DPwoD2cc.mjs → Checkmark-Cvb9TKPn.mjs} +1 -1
- package/dist/{Clock-C9YN2UE6.mjs → Clock-CRQ3vRb2.mjs} +1 -1
- package/dist/{Close-CuB41vwe.mjs → Close-CgKO-GmV.mjs} +1 -1
- package/dist/{CloudDownload-CxbXkl-A.mjs → CloudDownload-h8jFkmQV.mjs} +1 -1
- package/dist/{Comment-B70VK5iQ.mjs → Comment-CGL9Fj78.mjs} +1 -1
- package/dist/{Comments-B1eVtuj_.mjs → Comments-DnQeuBWu.mjs} +1 -1
- package/dist/{Connections-CgHfsXFl.mjs → Connections-GHPqdXk3.mjs} +1 -1
- package/dist/{Copy-BT-yQJw7.mjs → Copy-DSXLX28f.mjs} +1 -1
- package/dist/{CustomQuestion-DAwKynPQ.mjs → CustomQuestion-CYiCPJX8.mjs} +1 -1
- package/dist/{CustomerSuccess-1U9T-T1n.mjs → CustomerSuccess-FdMSyQLg.mjs} +1 -1
- package/dist/{DatePicker-BP1Bpv1b.mjs → DatePicker-0RNUxWxN.mjs} +1 -1
- package/dist/{Dot-CfD9KJ6k.mjs → Dot-CRWfXlj2.mjs} +1 -1
- package/dist/{Download-G0LOA1A3.mjs → Download-Be5-WujX.mjs} +1 -1
- package/dist/{Drag-C4fCOqTJ.mjs → Drag-wZ_mXU-D.mjs} +1 -1
- package/dist/{Earn-_iBKtfla.mjs → Earn-06RGCgPA.mjs} +1 -1
- package/dist/{Email-Cmm1RvmL.mjs → Email-8o3h5VSI.mjs} +1 -1
- package/dist/{EmojiDisplay-BZzD6ibf.mjs → EmojiDisplay-D3d36Nrl.mjs} +2 -2
- package/dist/{EmojiPicker-DMtVAC8n.mjs → EmojiPicker-BzkYk2Yv.mjs} +2 -2
- package/dist/{EnpsSurvey-DEMQkTOy.mjs → EnpsSurvey-wpqDe9Xq.mjs} +1 -1
- package/dist/{Expired-BflOG-Fm.mjs → Expired-DbhcQ39l.mjs} +1 -1
- package/dist/{Eye-B6MG7oI1.mjs → Eye-Dceyvp5y.mjs} +1 -1
- package/dist/{Feedback-qpfEKs95.mjs → Feedback-BQpvKfzY.mjs} +1 -1
- package/dist/{Feedback-DBmAcA2m.mjs → Feedback-DTwwTQN3.mjs} +3 -3
- package/dist/{Filter-BN9dK5kD.mjs → Filter-CKJYpt6s.mjs} +1 -1
- package/dist/{Flag-jKSpbvn6.mjs → Flag-Bex2EvDE.mjs} +1 -1
- package/dist/{Giphy-CXDFjiTX.mjs → Giphy-CFMqJSYK.mjs} +1 -1
- package/dist/{GoalCount-KUyaRVck.mjs → GoalCount-Bi0NMucg.mjs} +1 -1
- package/dist/{GoalPercent-DDsVzhO3.mjs → GoalPercent-B-kxyps2.mjs} +1 -1
- package/dist/{GoalTask-TDXXVUhs.mjs → GoalTask-IZwR0ZSf.mjs} +1 -1
- package/dist/{GoalTask-C3AKmm5p.mjs → GoalTask-Xjgp_Wo0.mjs} +1 -1
- package/dist/{Goals-a3Gd4p1n.mjs → Goals-BBGtKkJ1.mjs} +1 -1
- package/dist/{Goals-BnrsLd9r.mjs → Goals-COoucVqq.mjs} +1 -1
- package/dist/{Growth-CCeyxRJc.mjs → Growth-D8A13g-Q.mjs} +1 -1
- package/dist/{Hamburger-2QlPY7MK.mjs → Hamburger-DInD_hK4.mjs} +1 -1
- package/dist/{Help-BcXc8ez6.mjs → Help-BcQznZaK.mjs} +1 -1
- package/dist/{HighFive-UG0NAADf.mjs → HighFive-DqVD1Say.mjs} +1 -1
- package/dist/{HighFive-DCjp43gs.mjs → HighFive-kxxxx1a1.mjs} +1 -1
- package/dist/{Home-yMgsJvNt.mjs → Home-CE6c4US8.mjs} +1 -1
- package/dist/{Home-C5WodEqM.mjs → Home-Dzhzxm5B.mjs} +1 -1
- package/dist/{HorizontalLine-JD_RqEgw.mjs → HorizontalLine-Dyh1cfN-.mjs} +1 -1
- package/dist/{Hourglass-C3_IVNJa.mjs → Hourglass-CZ_gs0L5.mjs} +1 -1
- package/dist/{Image-CFKQTicl.mjs → Image-D8RpGjUr.mjs} +1 -1
- package/dist/{ImageFile-DfNJ9qhq.mjs → ImageFile-CpJ6rqlm.mjs} +1 -1
- package/dist/{InGoodCompany-6xQLozP6.mjs → InGoodCompany-F_ltthqt.mjs} +1 -1
- package/dist/{Incentives-DFFQFZih.mjs → Incentives-CFe-oLpH.mjs} +1 -1
- package/dist/{Info-DwqI5ZcJ.mjs → Info-DNU4voqh.mjs} +1 -1
- package/dist/{Insights-Cy8kUifj.mjs → Insights-C0x9QzfX.mjs} +1 -1
- package/dist/{Insights-Dx0mt_sq.mjs → Insights-D9LemXYM.mjs} +1 -1
- package/dist/{Integrations-BYYqLmX_.mjs → Integrations-D4MNE3-i.mjs} +1 -1
- package/dist/{KazooLogo-I7BBjIWv.mjs → KazooLogo-DaVNTRlw.mjs} +1 -1
- package/dist/{KnowYourAudience-CVAchlPu.mjs → KnowYourAudience-D9qjKO2J.mjs} +1 -1
- package/dist/{Launch-Da51LF1k.mjs → Launch-xm296JGJ.mjs} +1 -1
- package/dist/{Link-Bi1JqO19.mjs → Link-DUJoL5SM.mjs} +1 -1
- package/dist/{LinkArrow-a7xg2uw5.mjs → LinkArrow-C53WYuP9.mjs} +1 -1
- package/dist/{Loading-C1i-_OMU.mjs → Loading-YoU3Ro5C.mjs} +1 -1
- package/dist/{Lock-CU7TWBei.mjs → Lock-Ig2rvmxz.mjs} +1 -1
- package/dist/{Management-DcaxdfRe.mjs → Management-CSk1I3Vu.mjs} +1 -1
- package/dist/{MeatballMenu-CXyQkQTY.mjs → MeatballMenu-akus2Lt6.mjs} +1 -1
- package/dist/{Messages-BaRHnj7R.mjs → Messages-CdHryWCZ.mjs} +1 -1
- package/dist/{Mobile-C82mnkS8.mjs → Mobile-DIs7zF33.mjs} +1 -1
- package/dist/{Nomination-Clb3B7rU.mjs → Nomination-DtyERYRo.mjs} +1 -1
- package/dist/{NominationsAndAwards-LizaS-lj.mjs → NominationsAndAwards-DUlt_REW.mjs} +1 -1
- package/dist/{Notification-C-hvbQgF.mjs → Notification-yHY4Q-iO.mjs} +1 -1
- package/dist/{Okrs-Dz7iz6Y9.mjs → Okrs-BVR2trfg.mjs} +1 -1
- package/dist/{PeerToPeer-BvKH26DO.mjs → PeerToPeer-Dw7054su.mjs} +1 -1
- package/dist/{Pencil-CXJlOW6Z.mjs → Pencil--PMTRUju.mjs} +1 -1
- package/dist/{People--NThL2WE.mjs → People-01fveD3Z.mjs} +1 -1
- package/dist/{PerformanceManagement-Bb1JvSj_.mjs → PerformanceManagement-BO6JMhw0.mjs} +1 -1
- package/dist/{Pin-cuzusAoP.mjs → Pin-DrqWw6Ik.mjs} +1 -1
- package/dist/PinnedIconAward-CfI7QvmM.mjs +20 -0
- package/dist/PinnedIconBrightIdea-C-jLsEme.mjs +20 -0
- package/dist/PinnedIconBullseye-DHzPwFNh.mjs +36 -0
- package/dist/PinnedIconChampagne-DqhhML6p.mjs +20 -0
- package/dist/PinnedIconCheer-Dr-LynHf.mjs +20 -0
- package/dist/PinnedIconCrown-BjZF_Us9.mjs +20 -0
- package/dist/PinnedIconDiamond-mR8xcC_8.mjs +20 -0
- package/dist/PinnedIconHoleInOne-D0YlpLT_.mjs +20 -0
- package/dist/PinnedIconMedal-CsbWDOeO.mjs +20 -0
- package/dist/PinnedIconMetrics-FZwXRcdT.mjs +20 -0
- package/dist/PinnedIconPin-DQYaYwDa.mjs +20 -0
- package/dist/PinnedIconRockAndRoll-2pp2LlIt.mjs +23 -0
- package/dist/PinnedIconRocket-BIrfZPOc.mjs +20 -0
- package/dist/PinnedIconStrength-B2Jlgh2J.mjs +20 -0
- package/dist/PinnedIconTeddyBear-CywM8WU1.mjs +43 -0
- package/dist/{PlatformLogo-CK09vVsJ.mjs → PlatformLogo-D2gEkUsw.mjs} +1 -1
- package/dist/{Plus-DJaLCs4t.mjs → Plus-hKSiCxQa.mjs} +1 -1
- package/dist/{Popper-DTEvAyBx.mjs → Popper-D6_fRDiW.mjs} +1 -1
- package/dist/{PrivateEye-WzWeEjb1.mjs → PrivateEye-DOcfeBbQ.mjs} +1 -1
- package/dist/{PrivatePushPin-DnNGaLNq.mjs → PrivatePushPin-BORYdpxi.mjs} +1 -1
- package/dist/{Processing-BDUBy4UH.mjs → Processing-C_zBVZIK.mjs} +1 -1
- package/dist/{Productivity-D5BVYtaN.mjs → Productivity-CLljIpDf.mjs} +1 -1
- package/dist/{ProgressCircle-CFTLbp-e.mjs → ProgressCircle-B9baqpgb.mjs} +1 -1
- package/dist/{PulseSurvey-B1TvHyDt.mjs → PulseSurvey-BQdfWo9A.mjs} +1 -1
- package/dist/{PushPin-CvluYJjM.mjs → PushPin-DkTVqfKk.mjs} +1 -1
- package/dist/{Recognition-DmfePy_z.mjs → Recognition-D3KBTtXQ.mjs} +1 -1
- package/dist/{Recognition-BDpYBUvN.mjs → Recognition-Dz_nZRDX.mjs} +1 -1
- package/dist/{Retention-BkJo5gos.mjs → Retention-ChOlEHZk.mjs} +1 -1
- package/dist/{Rewards-CtGT7qtN.mjs → Rewards-CVe8-44F.mjs} +1 -1
- package/dist/{Rewards-D_TyBfCv.mjs → Rewards-DU-Ho2dk.mjs} +1 -1
- package/dist/{RewardsCatalog-CyMbCiki.mjs → RewardsCatalog-BV1YY9Iq.mjs} +1 -1
- package/dist/{Roadmap-djDY2KOo.mjs → Roadmap-zfvbEbqx.mjs} +1 -1
- package/dist/{Search-N21SDOKm.mjs → Search-BhJp-JJi.mjs} +1 -1
- package/dist/{Security-CExbN6aN.mjs → Security-DRtNtKPc.mjs} +1 -1
- package/dist/{SendRecognition-gKLXXcO-.mjs → SendRecognition-Byajzteg.mjs} +1 -1
- package/dist/{ShareLink-Bn_3CvlX.mjs → ShareLink-D5S-TmIH.mjs} +1 -1
- package/dist/{Skill-BMD-jOQ8.mjs → Skill-CimhEzRw.mjs} +1 -1
- package/dist/{Skills-3QOhEpbd.mjs → Skills-DaQLD2Bb.mjs} +1 -1
- package/dist/{Smiley-BVpBA18A.mjs → Smiley-BmRWMKju.mjs} +1 -1
- package/dist/{Stop-C1787JfZ.mjs → Stop-jEo5XX0_.mjs} +1 -1
- package/dist/{Survey-CKqWVVEo.mjs → Survey-D7Lx8jsj.mjs} +1 -1
- package/dist/{Surveys-DvOszZdj.mjs → Surveys-Bnwc-nKF.mjs} +1 -1
- package/dist/{SyncUps-WBKKUyeT.mjs → SyncUps-DRTsoQ40.mjs} +1 -1
- package/dist/{Syncs-C_fskEeo.mjs → Syncs-CND3di67.mjs} +1 -1
- package/dist/{TalentReview-Dyg4wS7c.mjs → TalentReview-BCPucb1o.mjs} +1 -1
- package/dist/{TalentReview-RkspiqWg.mjs → TalentReview-CnEteZI6.mjs} +1 -1
- package/dist/{TalentReviewInsights-BnGxBe_H.mjs → TalentReviewInsights-COIEM4Dx.mjs} +1 -1
- package/dist/{TaskCompleted-BzlFAImO.mjs → TaskCompleted-CVVIklMl.mjs} +1 -1
- package/dist/{Team-Dxwjirjk.mjs → Team-DG9N4ucC.mjs} +1 -1
- package/dist/{Teams-2ntLFtej.mjs → Teams-BksxxA72.mjs} +1 -1
- package/dist/{TextBold-O-BU1YXO.mjs → TextBold-JCoCqG2E.mjs} +1 -1
- package/dist/{TextBulletList-BqZE7Exr.mjs → TextBulletList-wdvz7YF6.mjs} +1 -1
- package/dist/{TextInput-CvxuF1db.mjs → TextInput-BOcHdYfi.mjs} +1 -1
- package/dist/{TextItalic-DvGZwAEM.mjs → TextItalic-DS-EVK3i.mjs} +1 -1
- package/dist/{TextNumberList-BjEHBnDb.mjs → TextNumberList-DN5rq8Yj.mjs} +1 -1
- package/dist/{TextUnderline-BztlEu7E.mjs → TextUnderline-BFQg7hSu.mjs} +1 -1
- package/dist/{Toggle-BwrTz2fn.mjs → Toggle-CFvYpFN8.mjs} +1 -1
- package/dist/{Trash-6WVc1Zo7.mjs → Trash-CS1BAv8d.mjs} +1 -1
- package/dist/{TrendDown-C6wcWlmc.mjs → TrendDown-C8RVuMbz.mjs} +1 -1
- package/dist/{TrendUp-BWejhArt.mjs → TrendUp-eYJN1A6n.mjs} +1 -1
- package/dist/{Unplugged-0SGS9CKk.mjs → Unplugged-j7jw4iIL.mjs} +1 -1
- package/dist/{UpDownCaret-ClBkt-ma.mjs → UpDownCaret-DdXKQT69.mjs} +1 -1
- package/dist/{VerticalLine-DjZxhqxX.mjs → VerticalLine-DU1-BZfR.mjs} +1 -1
- package/dist/{Warning-Bj_gmfqP.mjs → Warning-C1kQzf8A.mjs} +1 -1
- package/dist/{WarningNoOutline-DBvoW0_1.mjs → WarningNoOutline-CXC1MVzS.mjs} +1 -1
- package/dist/{YesAnd-COl6p1Q_.mjs → YesAnd-C18ssrgG.mjs} +1 -1
- package/dist/ai-assistant.css +645 -0
- package/dist/{emoji-BYYHYzOu.mjs → emoji-CJuut-ic.mjs} +1 -88
- package/dist/react.es.js +5 -0
- package/dist/{ai-assistant.umd.js → react.umd.js} +15685 -1695
- package/dist/{reactComponent-DigPLrfy.mjs → reactComponent-B4VA_e0q.mjs} +15712 -1646
- package/express.js +2 -2
- package/package.json +10 -5
- package/react.js +1 -1
- package/src/backend/express.test.ts +177 -10
- package/src/backend/express.ts +93 -12
- package/src/frontend/AiAssistant.test.tsx +1 -1
- package/src/frontend/AiAssistant.tsx +9 -2
- package/src/frontend/components/AiAssistantComponent.scss +24 -0
- package/src/frontend/components/AiAssistantComponent.test.tsx +262 -3
- package/src/frontend/components/AiAssistantComponent.tsx +238 -10
- package/src/frontend/components/messageRenderers/AssistantMessage.test.tsx +154 -0
- package/src/frontend/components/messageRenderers/AssistantMessage.tsx +104 -0
- package/src/frontend/components/messageRenderers/UserMessage.test.tsx +103 -0
- package/src/frontend/components/messageRenderers/UserMessage.tsx +43 -0
- package/src/frontend/reactComponent.ts +2 -0
- package/src/frontend/tools/generatedSdks.ts +43 -0
- package/src/frontend/tools/llm-tools/getCurrentUser.test.ts +100 -0
- package/src/frontend/tools/llm-tools/getCurrentUser.ts +158 -0
- package/src/frontend/tools/llm-tools/index.ts +6 -0
- package/src/frontend/tools/llm-tools/kazooPlatform/getRewardsAndRecognitionCurrentUser.test.ts +76 -0
- package/src/frontend/tools/llm-tools/kazooPlatform/getRewardsAndRecognitionCurrentUser.ts +36 -0
- package/src/frontend/tools/llm-tools/pulsePlatform/.gitkeep +0 -0
- package/src/frontend/tools/llm-tools/shared/.gitkeep +0 -0
- package/src/frontend/tools/useAiAssistantSession.test.ts +768 -0
- package/src/frontend/tools/useAiAssistantSession.ts +374 -0
- package/types.d.ts +67 -1
- package/dist/PinnedIconAward-CWgNNAPU.mjs +0 -20
- package/dist/PinnedIconBrightIdea-Cj1UUoKb.mjs +0 -20
- package/dist/PinnedIconBullseye-SN7Ps5fC.mjs +0 -20
- package/dist/PinnedIconChampagne-CMj1TZw7.mjs +0 -20
- package/dist/PinnedIconCheer-osAldHSN.mjs +0 -20
- package/dist/PinnedIconCrown-u0dSUhVZ.mjs +0 -20
- package/dist/PinnedIconDiamond-Byiswgk3.mjs +0 -20
- package/dist/PinnedIconHoleInOne-DzSbNQ5B.mjs +0 -20
- package/dist/PinnedIconMedal-YQYBs9V1.mjs +0 -20
- package/dist/PinnedIconMetrics-D2Li4Bnf.mjs +0 -20
- package/dist/PinnedIconPin-Xxp0coP8.mjs +0 -20
- package/dist/PinnedIconRockAndRoll-DlbejnZU.mjs +0 -23
- package/dist/PinnedIconRocket-B9dqD0uv.mjs +0 -42
- package/dist/PinnedIconStrength-BXA19IfC.mjs +0 -20
- package/dist/PinnedIconTeddyBear-D2n83xPZ.mjs +0 -20
- package/dist/ai-assistant.es.js +0 -4
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
import { micromark } from "micromark";
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
Flex,
|
|
7
|
+
Spacer,
|
|
8
|
+
UtilityText,
|
|
9
|
+
Text,
|
|
10
|
+
Pill,
|
|
11
|
+
CardPadding,
|
|
12
|
+
Icon,
|
|
13
|
+
} from "@kazoohr/confetti";
|
|
14
|
+
|
|
15
|
+
import { type ChatItemWithTimestamp } from "../../tools/useAiAssistantSession";
|
|
16
|
+
|
|
17
|
+
export function AssistantMessage({
|
|
18
|
+
message,
|
|
19
|
+
}: {
|
|
20
|
+
message: ChatItemWithTimestamp;
|
|
21
|
+
}) {
|
|
22
|
+
const aggregatedToolCalls = message.parts.filter(
|
|
23
|
+
(part) => part.type === "toolCall"
|
|
24
|
+
);
|
|
25
|
+
const aggregatedTextParts = message.parts
|
|
26
|
+
.filter((part) => part.type === "text")
|
|
27
|
+
.map((text) => text.text)
|
|
28
|
+
.join("");
|
|
29
|
+
|
|
30
|
+
const streamingTextResizingContainerRef = React.useRef<HTMLDivElement>(null);
|
|
31
|
+
const streamingTextRef = React.useRef<HTMLDivElement>(null);
|
|
32
|
+
|
|
33
|
+
const componentsToRender: React.ReactNode[] = [];
|
|
34
|
+
|
|
35
|
+
React.useEffect(() => {
|
|
36
|
+
if (streamingTextRef.current && streamingTextResizingContainerRef.current) {
|
|
37
|
+
const textHeight = streamingTextRef.current.clientHeight;
|
|
38
|
+
streamingTextResizingContainerRef.current.style.height = `${textHeight}px`;
|
|
39
|
+
}
|
|
40
|
+
}, [aggregatedTextParts]);
|
|
41
|
+
|
|
42
|
+
if (aggregatedTextParts) {
|
|
43
|
+
componentsToRender.push(
|
|
44
|
+
<div
|
|
45
|
+
ref={streamingTextResizingContainerRef}
|
|
46
|
+
className="streaming-text-resizing-container"
|
|
47
|
+
key="text"
|
|
48
|
+
>
|
|
49
|
+
<div ref={streamingTextRef}>
|
|
50
|
+
<CardPadding>
|
|
51
|
+
<Flex gap="16px" alignItems="center">
|
|
52
|
+
<Icon icon="teams" />
|
|
53
|
+
<Text bold size="small">
|
|
54
|
+
assistant
|
|
55
|
+
</Text>
|
|
56
|
+
<UtilityText size="extraSmall">
|
|
57
|
+
{message.timestamp?.toLocaleString()}
|
|
58
|
+
</UtilityText>
|
|
59
|
+
</Flex>
|
|
60
|
+
<Spacer />
|
|
61
|
+
<div
|
|
62
|
+
className="markdown"
|
|
63
|
+
dangerouslySetInnerHTML={{
|
|
64
|
+
__html: micromark(aggregatedTextParts),
|
|
65
|
+
}}
|
|
66
|
+
/>
|
|
67
|
+
</CardPadding>
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (aggregatedToolCalls.length > 0) {
|
|
74
|
+
componentsToRender.push(
|
|
75
|
+
<div key="tools">
|
|
76
|
+
{aggregatedToolCalls.map((toolCall) => {
|
|
77
|
+
return (
|
|
78
|
+
<CardPadding key={toolCall.toolName}>
|
|
79
|
+
<div
|
|
80
|
+
style={{ textAlign: "right" }}
|
|
81
|
+
title={`Tool call: ${toolCall.toolName}`}
|
|
82
|
+
>
|
|
83
|
+
<div style={{ display: "inline-block" }}>
|
|
84
|
+
<Pill
|
|
85
|
+
icon="info"
|
|
86
|
+
size="large"
|
|
87
|
+
text="Fetch data"
|
|
88
|
+
variant="outline"
|
|
89
|
+
/>
|
|
90
|
+
</div>
|
|
91
|
+
</div>
|
|
92
|
+
</CardPadding>
|
|
93
|
+
);
|
|
94
|
+
})}
|
|
95
|
+
</div>
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (componentsToRender.length === 0) {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return <>{componentsToRender}</>;
|
|
104
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
import { render, screen } from "@kazoohr/test";
|
|
4
|
+
|
|
5
|
+
import { type ChatItemWithTimestamp } from "../../tools/useAiAssistantSession";
|
|
6
|
+
|
|
7
|
+
import { UserMessage } from "./UserMessage";
|
|
8
|
+
|
|
9
|
+
jest.mock("micromark", () => ({
|
|
10
|
+
micromark: (text: string) => text,
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
describe("UserMessage", () => {
|
|
14
|
+
const mockTimestamp = new Date("2024-01-01T12:00:00Z");
|
|
15
|
+
|
|
16
|
+
it("renders text message correctly", () => {
|
|
17
|
+
const message: ChatItemWithTimestamp = {
|
|
18
|
+
role: "user",
|
|
19
|
+
timestamp: mockTimestamp,
|
|
20
|
+
parts: [
|
|
21
|
+
{
|
|
22
|
+
type: "text",
|
|
23
|
+
text: "Hello world",
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
render(<UserMessage message={message} />);
|
|
29
|
+
|
|
30
|
+
expect(screen.getByText("you")).toBeInTheDocument();
|
|
31
|
+
expect(screen.getByText("Hello world")).toBeInTheDocument();
|
|
32
|
+
expect(
|
|
33
|
+
screen.getByText(mockTimestamp.toLocaleString())
|
|
34
|
+
).toBeInTheDocument();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("renders markdown text correctly", () => {
|
|
38
|
+
const message: ChatItemWithTimestamp = {
|
|
39
|
+
role: "user",
|
|
40
|
+
timestamp: mockTimestamp,
|
|
41
|
+
parts: [
|
|
42
|
+
{
|
|
43
|
+
type: "text",
|
|
44
|
+
text: "# Hello\n\n**Bold text**",
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
render(<UserMessage message={message} />);
|
|
50
|
+
|
|
51
|
+
const content = document.querySelector(".markdown");
|
|
52
|
+
expect(content).toBeInTheDocument();
|
|
53
|
+
expect(content?.innerHTML).toContain("# Hello\n\n**Bold text**");
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("returns null for empty message parts", () => {
|
|
57
|
+
const message: ChatItemWithTimestamp = {
|
|
58
|
+
role: "user",
|
|
59
|
+
timestamp: mockTimestamp,
|
|
60
|
+
parts: [],
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const { container } = render(<UserMessage message={message} />);
|
|
64
|
+
expect(container.firstChild).toBeNull();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("aggregates multiple text parts", () => {
|
|
68
|
+
const message: ChatItemWithTimestamp = {
|
|
69
|
+
role: "user",
|
|
70
|
+
timestamp: mockTimestamp,
|
|
71
|
+
parts: [
|
|
72
|
+
{
|
|
73
|
+
type: "text",
|
|
74
|
+
text: "Hello",
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
type: "text",
|
|
78
|
+
text: " world",
|
|
79
|
+
},
|
|
80
|
+
],
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
render(<UserMessage message={message} />);
|
|
84
|
+
expect(screen.getByText("Hello world")).toBeInTheDocument();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("ignores non-text parts", () => {
|
|
88
|
+
const message: ChatItemWithTimestamp = {
|
|
89
|
+
role: "user",
|
|
90
|
+
timestamp: mockTimestamp,
|
|
91
|
+
parts: [
|
|
92
|
+
{
|
|
93
|
+
type: "toolCall",
|
|
94
|
+
toolName: "testTool",
|
|
95
|
+
args: { test: true },
|
|
96
|
+
},
|
|
97
|
+
],
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const { container } = render(<UserMessage message={message} />);
|
|
101
|
+
expect(container.firstChild).toBeNull();
|
|
102
|
+
});
|
|
103
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
import { micromark } from "micromark";
|
|
4
|
+
|
|
5
|
+
import { Card, Flex, Spacer, UtilityText, Text, Icon } from "@kazoohr/confetti";
|
|
6
|
+
|
|
7
|
+
import { ChatItemWithTimestamp } from "../../tools/useAiAssistantSession";
|
|
8
|
+
|
|
9
|
+
export function UserMessage({ message }: { message: ChatItemWithTimestamp }) {
|
|
10
|
+
const aggregatedTextParts = message.parts
|
|
11
|
+
.filter((part) => part.type === "text")
|
|
12
|
+
.map((text) => text.text)
|
|
13
|
+
.join("");
|
|
14
|
+
|
|
15
|
+
const componentsToRender: React.ReactNode[] = [];
|
|
16
|
+
|
|
17
|
+
if (aggregatedTextParts) {
|
|
18
|
+
componentsToRender.push(
|
|
19
|
+
<Card key="text" noShadow withBorder>
|
|
20
|
+
<Flex gap="16px" alignItems="center">
|
|
21
|
+
<Icon icon="feedback" />
|
|
22
|
+
<Text bold size="small">
|
|
23
|
+
you
|
|
24
|
+
</Text>
|
|
25
|
+
<UtilityText size="extraSmall">
|
|
26
|
+
{message.timestamp?.toLocaleString()}
|
|
27
|
+
</UtilityText>
|
|
28
|
+
</Flex>
|
|
29
|
+
<Spacer />
|
|
30
|
+
<div
|
|
31
|
+
className="markdown"
|
|
32
|
+
dangerouslySetInnerHTML={{ __html: micromark(aggregatedTextParts) }}
|
|
33
|
+
/>
|
|
34
|
+
</Card>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (componentsToRender.length === 0) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return <>{componentsToRender}</>;
|
|
43
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// istanbul ignore file - light wrapper around the generated SDK
|
|
2
|
+
|
|
3
|
+
import { DocumentNode, print } from "@kazoohr/graphql";
|
|
4
|
+
|
|
5
|
+
import { getSdk as getStitchedSdk } from "../../../generated/stitchedSdk";
|
|
6
|
+
|
|
7
|
+
export const stitchedSdk = {
|
|
8
|
+
...getStitchedSdk((query, variables) => {
|
|
9
|
+
return fetcher({
|
|
10
|
+
url: "/graphql",
|
|
11
|
+
query,
|
|
12
|
+
variables,
|
|
13
|
+
});
|
|
14
|
+
}),
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export async function fetcher({
|
|
18
|
+
query,
|
|
19
|
+
url,
|
|
20
|
+
variables,
|
|
21
|
+
}: {
|
|
22
|
+
url: string;
|
|
23
|
+
query: DocumentNode;
|
|
24
|
+
variables?: any;
|
|
25
|
+
}): Promise<any> {
|
|
26
|
+
const response = await fetch(url, {
|
|
27
|
+
method: "POST",
|
|
28
|
+
headers: {
|
|
29
|
+
"Content-Type": "application/json",
|
|
30
|
+
Accept: "application/graphql-response+json",
|
|
31
|
+
},
|
|
32
|
+
body: JSON.stringify({
|
|
33
|
+
query: print(query),
|
|
34
|
+
variables,
|
|
35
|
+
}),
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
if (!response.ok) {
|
|
39
|
+
throw new Error("Network response returned non-OK status");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return await response.json();
|
|
43
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { getCurrentUserTool, resetCurrentUser } from "./getCurrentUser";
|
|
2
|
+
|
|
3
|
+
const mockFetch = jest.fn();
|
|
4
|
+
global.fetch = mockFetch;
|
|
5
|
+
|
|
6
|
+
const mockConsoleLog = jest.fn();
|
|
7
|
+
global.console.log = mockConsoleLog;
|
|
8
|
+
|
|
9
|
+
describe("getCurrentUserTool", () => {
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
jest.clearAllMocks();
|
|
12
|
+
resetCurrentUser();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it("fetches and caches user data", async () => {
|
|
16
|
+
const mockUserData = {
|
|
17
|
+
data: {
|
|
18
|
+
directory_me: {
|
|
19
|
+
id: "123",
|
|
20
|
+
name: "Test User",
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
mockFetch.mockResolvedValueOnce({
|
|
26
|
+
json: () => Promise.resolve(mockUserData),
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// First call should make the fetch request
|
|
30
|
+
const result1 = await getCurrentUserTool.fulfill({});
|
|
31
|
+
expect(result1).toBe(JSON.stringify(mockUserData));
|
|
32
|
+
expect(mockFetch).toHaveBeenCalledTimes(1);
|
|
33
|
+
expect(mockFetch).toHaveBeenCalledWith("/graphql", {
|
|
34
|
+
method: "POST",
|
|
35
|
+
credentials: "include",
|
|
36
|
+
headers: {
|
|
37
|
+
"Content-Type": "application/json",
|
|
38
|
+
},
|
|
39
|
+
body: expect.any(String),
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// Second call should use cached value
|
|
43
|
+
const result2 = await getCurrentUserTool.fulfill({});
|
|
44
|
+
expect(result2).toBe(JSON.stringify(mockUserData));
|
|
45
|
+
expect(mockFetch).toHaveBeenCalledTimes(1); // Should not make another fetch
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("handles fetch errors", async () => {
|
|
49
|
+
const errorMessage = "Network error";
|
|
50
|
+
const error = new Error(errorMessage);
|
|
51
|
+
mockFetch.mockRejectedValueOnce(error);
|
|
52
|
+
|
|
53
|
+
const result = await getCurrentUserTool.fulfill({});
|
|
54
|
+
expect(result).toBe(JSON.stringify({ error: errorMessage }));
|
|
55
|
+
expect(mockFetch).toHaveBeenCalledTimes(1);
|
|
56
|
+
expect(mockConsoleLog).not.toHaveBeenCalled();
|
|
57
|
+
|
|
58
|
+
// Next call should try fetching again since error cleared the cache
|
|
59
|
+
const mockUserData = { data: { directory_me: { id: "123" } } };
|
|
60
|
+
mockFetch.mockResolvedValueOnce({
|
|
61
|
+
json: () => Promise.resolve(mockUserData),
|
|
62
|
+
});
|
|
63
|
+
const result2 = await getCurrentUserTool.fulfill({});
|
|
64
|
+
expect(result2).toBe(JSON.stringify(mockUserData));
|
|
65
|
+
expect(mockFetch).toHaveBeenCalledTimes(2);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("handles non-Error fetch rejections", async () => {
|
|
69
|
+
mockFetch.mockRejectedValueOnce("Non-error rejection");
|
|
70
|
+
|
|
71
|
+
const result = await getCurrentUserTool.fulfill({});
|
|
72
|
+
expect(result).toBe(JSON.stringify({ error: "Non-error rejection" }));
|
|
73
|
+
expect(mockFetch).toHaveBeenCalledTimes(1);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("handles JSON parse errors", async () => {
|
|
77
|
+
mockFetch.mockResolvedValueOnce({
|
|
78
|
+
json: () => Promise.reject(new Error("Invalid JSON")),
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const result = await getCurrentUserTool.fulfill({});
|
|
82
|
+
expect(result).toBe(JSON.stringify({ error: "Invalid JSON" }));
|
|
83
|
+
expect(mockFetch).toHaveBeenCalledTimes(1);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("handles non-Error JSON parse errors", async () => {
|
|
87
|
+
mockFetch.mockResolvedValueOnce({
|
|
88
|
+
json: () => Promise.reject("Invalid JSON string"),
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
const result = await getCurrentUserTool.fulfill({});
|
|
92
|
+
expect(result).toBe(JSON.stringify({ error: "Invalid JSON string" }));
|
|
93
|
+
expect(mockFetch).toHaveBeenCalledTimes(1);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("has correct tool metadata", () => {
|
|
97
|
+
expect(getCurrentUserTool.name).toBe("getCurrentUser");
|
|
98
|
+
expect(getCurrentUserTool.description).toBe("Get the current user");
|
|
99
|
+
});
|
|
100
|
+
});
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { handleForegroundPromise } from "@kazoohr/helpers";
|
|
2
|
+
import { createTool } from "@kazoohr/llm/createTool";
|
|
3
|
+
|
|
4
|
+
export let currentUser: Promise<string> | undefined = undefined;
|
|
5
|
+
|
|
6
|
+
export function resetCurrentUser() {
|
|
7
|
+
currentUser = undefined;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const getCurrentUserTool = createTool({
|
|
11
|
+
name: "getCurrentUser",
|
|
12
|
+
description: "Get the current user",
|
|
13
|
+
fulfill: async function fulfillGetCurrentUser() {
|
|
14
|
+
if (currentUser) {
|
|
15
|
+
return await currentUser;
|
|
16
|
+
}
|
|
17
|
+
currentUser = new Promise((resolve) => {
|
|
18
|
+
handleForegroundPromise(async (): Promise<unknown> => {
|
|
19
|
+
try {
|
|
20
|
+
// First, introspect `/api` endpoint to get the schema
|
|
21
|
+
const response = await fetch("/graphql", {
|
|
22
|
+
method: "POST",
|
|
23
|
+
credentials: "include",
|
|
24
|
+
headers: {
|
|
25
|
+
"Content-Type": "application/json",
|
|
26
|
+
},
|
|
27
|
+
body: JSON.stringify({
|
|
28
|
+
query: currentUserQuery,
|
|
29
|
+
}),
|
|
30
|
+
}).catch((error: Error | string) => error);
|
|
31
|
+
if (response instanceof Error || typeof response === "string") {
|
|
32
|
+
currentUser = undefined;
|
|
33
|
+
resolve(
|
|
34
|
+
JSON.stringify({
|
|
35
|
+
error: response instanceof Error ? response.message : response,
|
|
36
|
+
})
|
|
37
|
+
);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const schema = await response.json();
|
|
41
|
+
resolve(JSON.stringify(schema));
|
|
42
|
+
} catch (error) {
|
|
43
|
+
currentUser = undefined;
|
|
44
|
+
resolve(
|
|
45
|
+
JSON.stringify({
|
|
46
|
+
error: error instanceof Error ? error.message : String(error),
|
|
47
|
+
})
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
return currentUser;
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const currentUserQuery = `
|
|
57
|
+
query GetCurrentUser {
|
|
58
|
+
directory_me {
|
|
59
|
+
avatar
|
|
60
|
+
adminOf {
|
|
61
|
+
departments {
|
|
62
|
+
id
|
|
63
|
+
name
|
|
64
|
+
}
|
|
65
|
+
locations {
|
|
66
|
+
id
|
|
67
|
+
name
|
|
68
|
+
}
|
|
69
|
+
groups {
|
|
70
|
+
id
|
|
71
|
+
name
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
birthday {
|
|
75
|
+
month
|
|
76
|
+
day
|
|
77
|
+
}
|
|
78
|
+
companyInfo {
|
|
79
|
+
customEula
|
|
80
|
+
customAcceptTermsMessage
|
|
81
|
+
name
|
|
82
|
+
allowUsersToShareInfo
|
|
83
|
+
allowProfilePictureUploads
|
|
84
|
+
kwProfileSettings {
|
|
85
|
+
canShareBirthday
|
|
86
|
+
canShareAnniversary
|
|
87
|
+
canShareBio
|
|
88
|
+
canChangeAvatar
|
|
89
|
+
canChangePreferredName
|
|
90
|
+
}
|
|
91
|
+
timezone
|
|
92
|
+
}
|
|
93
|
+
location
|
|
94
|
+
department
|
|
95
|
+
departmentInfo {
|
|
96
|
+
id
|
|
97
|
+
name
|
|
98
|
+
}
|
|
99
|
+
firstName
|
|
100
|
+
preferredName
|
|
101
|
+
id
|
|
102
|
+
ids {
|
|
103
|
+
kazooId
|
|
104
|
+
rrId
|
|
105
|
+
yeiImpersonatingUserId
|
|
106
|
+
yeiCompanyId
|
|
107
|
+
}
|
|
108
|
+
email
|
|
109
|
+
image30
|
|
110
|
+
image70
|
|
111
|
+
image160
|
|
112
|
+
avatar
|
|
113
|
+
initials
|
|
114
|
+
title
|
|
115
|
+
jobTitle: title
|
|
116
|
+
name
|
|
117
|
+
roles
|
|
118
|
+
isManager
|
|
119
|
+
startDate
|
|
120
|
+
groupFeatureFlags {
|
|
121
|
+
performance {
|
|
122
|
+
adhocGoal
|
|
123
|
+
brandedEmail
|
|
124
|
+
enablePerform
|
|
125
|
+
goalLibrary
|
|
126
|
+
syncUp
|
|
127
|
+
continuousFeedback
|
|
128
|
+
feedback360
|
|
129
|
+
requestFeedback
|
|
130
|
+
giveFeedback
|
|
131
|
+
recognition2
|
|
132
|
+
enableFeedback
|
|
133
|
+
enableGoals
|
|
134
|
+
enablePoints
|
|
135
|
+
enableLanguages
|
|
136
|
+
enableLocation
|
|
137
|
+
enablePoints
|
|
138
|
+
enablePolling
|
|
139
|
+
feedbackCheckin
|
|
140
|
+
}
|
|
141
|
+
recognition
|
|
142
|
+
}
|
|
143
|
+
permissionsInGroup {
|
|
144
|
+
performance
|
|
145
|
+
}
|
|
146
|
+
points {
|
|
147
|
+
givingBalance
|
|
148
|
+
redemptionBalance
|
|
149
|
+
}
|
|
150
|
+
showTermsConditionsModal
|
|
151
|
+
status
|
|
152
|
+
following
|
|
153
|
+
enabledModules
|
|
154
|
+
source
|
|
155
|
+
lastName
|
|
156
|
+
secondaryManagerOf
|
|
157
|
+
}
|
|
158
|
+
}`;
|
package/src/frontend/tools/llm-tools/kazooPlatform/getRewardsAndRecognitionCurrentUser.test.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { stitchedSdk } from "../../generatedSdks";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
getRewardsAndRecognitionCurrentUserTool,
|
|
5
|
+
resetCurrentUser,
|
|
6
|
+
} from "./getRewardsAndRecognitionCurrentUser";
|
|
7
|
+
|
|
8
|
+
jest.mock("../../generatedSdks", () => ({
|
|
9
|
+
stitchedSdk: {
|
|
10
|
+
GetCurrentUser: jest.fn(),
|
|
11
|
+
},
|
|
12
|
+
}));
|
|
13
|
+
|
|
14
|
+
const mockGetCurrentUser = stitchedSdk.GetCurrentUser as jest.Mock;
|
|
15
|
+
|
|
16
|
+
describe("getRewardsAndRecognitionCurrentUserTool", () => {
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
jest.clearAllMocks();
|
|
19
|
+
resetCurrentUser();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("fetches and caches user data", async () => {
|
|
23
|
+
const mockUserData = {
|
|
24
|
+
data: {
|
|
25
|
+
directory_me: {
|
|
26
|
+
id: "123",
|
|
27
|
+
name: "Test User",
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
mockGetCurrentUser.mockResolvedValueOnce(mockUserData);
|
|
33
|
+
|
|
34
|
+
// First call should make the execute request
|
|
35
|
+
const result1 = await getRewardsAndRecognitionCurrentUserTool.fulfill({});
|
|
36
|
+
expect(result1).toBe(JSON.stringify(mockUserData));
|
|
37
|
+
expect(mockGetCurrentUser).toHaveBeenCalledTimes(1);
|
|
38
|
+
|
|
39
|
+
// Second call should use cached value
|
|
40
|
+
const result2 = await getRewardsAndRecognitionCurrentUserTool.fulfill({});
|
|
41
|
+
expect(result2).toBe(JSON.stringify(mockUserData));
|
|
42
|
+
expect(mockGetCurrentUser).toHaveBeenCalledTimes(1); // Should not make another execute call
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("handles execute errors", async () => {
|
|
46
|
+
const errorMessage = "Network error";
|
|
47
|
+
const error = new Error(errorMessage);
|
|
48
|
+
mockGetCurrentUser.mockRejectedValueOnce(error);
|
|
49
|
+
|
|
50
|
+
const result = await getRewardsAndRecognitionCurrentUserTool.fulfill({});
|
|
51
|
+
expect(result).toBe(JSON.stringify({ error: errorMessage }));
|
|
52
|
+
expect(mockGetCurrentUser).toHaveBeenCalledTimes(1);
|
|
53
|
+
|
|
54
|
+
// Next call should try executing again since error cleared the cache
|
|
55
|
+
const mockUserData = { data: { directory_me: { id: "123" } } };
|
|
56
|
+
mockGetCurrentUser.mockResolvedValueOnce(mockUserData);
|
|
57
|
+
const result2 = await getRewardsAndRecognitionCurrentUserTool.fulfill({});
|
|
58
|
+
expect(result2).toBe(JSON.stringify(mockUserData));
|
|
59
|
+
expect(mockGetCurrentUser).toHaveBeenCalledTimes(2);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("handles non-Error execute rejections", async () => {
|
|
63
|
+
mockGetCurrentUser.mockRejectedValueOnce(new Error("Non-error rejection"));
|
|
64
|
+
|
|
65
|
+
const result = await getRewardsAndRecognitionCurrentUserTool.fulfill({});
|
|
66
|
+
expect(result).toBe(JSON.stringify({ error: "Non-error rejection" }));
|
|
67
|
+
expect(mockGetCurrentUser).toHaveBeenCalledTimes(1);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("has correct tool metadata", () => {
|
|
71
|
+
expect(getRewardsAndRecognitionCurrentUserTool.name).toBe("getCurrentUser");
|
|
72
|
+
expect(getRewardsAndRecognitionCurrentUserTool.description).toContain(
|
|
73
|
+
"Get the current user"
|
|
74
|
+
);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { handleForegroundPromise } from "@kazoohr/helpers";
|
|
2
|
+
import { createTool } from "@kazoohr/llm/createTool";
|
|
3
|
+
|
|
4
|
+
import { stitchedSdk } from "../../generatedSdks";
|
|
5
|
+
|
|
6
|
+
export let currentUser: Promise<string> | undefined = undefined;
|
|
7
|
+
|
|
8
|
+
export function resetCurrentUser() {
|
|
9
|
+
currentUser = undefined;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const getRewardsAndRecognitionCurrentUserTool = createTool({
|
|
13
|
+
name: "getCurrentUser",
|
|
14
|
+
description: "Get the current user from the Rewards and Recognition API",
|
|
15
|
+
fulfill: async function fulfillGetCurrentUser() {
|
|
16
|
+
if (currentUser) {
|
|
17
|
+
return await currentUser;
|
|
18
|
+
}
|
|
19
|
+
currentUser = new Promise((resolve) => {
|
|
20
|
+
handleForegroundPromise(async () => {
|
|
21
|
+
try {
|
|
22
|
+
const response = await stitchedSdk.GetCurrentUser();
|
|
23
|
+
resolve(JSON.stringify(response));
|
|
24
|
+
} catch (error) {
|
|
25
|
+
currentUser = undefined;
|
|
26
|
+
resolve(
|
|
27
|
+
JSON.stringify({
|
|
28
|
+
error: error?.message,
|
|
29
|
+
})
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
return currentUser;
|
|
35
|
+
},
|
|
36
|
+
});
|
|
File without changes
|
|
File without changes
|