From b1781058a587ea8f74a047308cbe67b6cc5117de Mon Sep 17 00:00:00 2001 From: Karthik Kalyanaraman Date: Thu, 4 Apr 2024 00:01:40 -0700 Subject: [PATCH 1/4] Pagination bug --- components/project/eval/eval.tsx | 4 ++-- components/project/eval/prompts.tsx | 4 ++-- lib/services/trace_service.ts | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/components/project/eval/eval.tsx b/components/project/eval/eval.tsx index 8441bfdc..7b139ddd 100644 --- a/components/project/eval/eval.tsx +++ b/components/project/eval/eval.tsx @@ -62,9 +62,9 @@ export default function Eval({ email }: { email: string }) { } if (result) { if (data) { - setData((prevData: any) => [...prevData, ...result.prompts.result]); + setData((prevData: any) => [...prevData, ...result?.prompts?.result || []]); } else { - setData(result.prompts.result); + setData(result?.prompts?.result || []); } } setPage((currentPage) => currentPage + 1); diff --git a/components/project/eval/prompts.tsx b/components/project/eval/prompts.tsx index bb385183..9ea37d51 100644 --- a/components/project/eval/prompts.tsx +++ b/components/project/eval/prompts.tsx @@ -52,9 +52,9 @@ export default function Prompts({ email }: { email: string }) { } if (result) { if (data) { - setData((prevData: any) => [...prevData, ...result.prompts.result]); + setData((prevData: any) => [...prevData, ...result?.prompts?.result || []]); } else { - setData(result.prompts.result); + setData(result?.prompts?.result || []); } } setPage((currentPage) => currentPage + 1); diff --git a/lib/services/trace_service.ts b/lib/services/trace_service.ts index 1761aa06..ad7c35c7 100644 --- a/lib/services/trace_service.ts +++ b/lib/services/trace_service.ts @@ -244,7 +244,7 @@ export class TraceService implements ITraceService { const md = { page, page_size: pageSize, total_pages: totalPages }; if (page! > totalPages) { - throw Error("Page number is greater than total pages"); + page = totalPages; } const query = sql.select( `* FROM ${project_id} WHERE attributes LIKE '%${attribute}%' ORDER BY 'start_time' DESC LIMIT ${pageSize} OFFSET ${ @@ -325,7 +325,7 @@ export class TraceService implements ITraceService { const md = { page, page_size: pageSize, total_pages: totalPages }; if (page! > totalPages) { - throw Error("Page number is greater than total pages"); + page = totalPages; } const query = sql.select( `* FROM ${project_id} ORDER BY 'createdAt' DESC LIMIT ${pageSize} OFFSET ${ From 0e963e226af17ffdba6282496d1979c6e30c20ad Mon Sep 17 00:00:00 2001 From: Karthik Kalyanaraman Date: Thu, 4 Apr 2024 17:13:49 -0700 Subject: [PATCH 2/4] Bug fix --- app/api/user/route.ts | 1 - components/project/traces.tsx | 2 -- lib/utils.ts | 1 + 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/app/api/user/route.ts b/app/api/user/route.ts index d7e95f34..a10d34bf 100644 --- a/app/api/user/route.ts +++ b/app/api/user/route.ts @@ -83,7 +83,6 @@ export async function PUT(req: NextRequest) { } if ("status" in data) { - console.log("updating status"); const user = await prisma.user.update({ where: { id, diff --git a/components/project/traces.tsx b/components/project/traces.tsx index 523aa352..1f56c03e 100644 --- a/components/project/traces.tsx +++ b/components/project/traces.tsx @@ -80,8 +80,6 @@ export default function Traces({ email }: { email: string }) { } // Merge the new data with the existing data - console.log("currentData", currentData); - console.log("newData", newData); if (currentData.length > 0) { const updatedData = [...currentData, ...newData]; diff --git a/lib/utils.ts b/lib/utils.ts index 584d22f2..2c6fd519 100644 --- a/lib/utils.ts +++ b/lib/utils.ts @@ -326,6 +326,7 @@ export function calculatePriceFromUsage( output_tokens: number; } ): any { + if (!model) return { total: 0, input: 0, output: 0 }; if (vendor === "openai") { const costTable = OPENAI_PRICING[model.includes("gpt-4") ? "gpt-4" : model]; if (costTable) { From e81f77d0c5037c983ee739355a89342a5a696090 Mon Sep 17 00:00:00 2001 From: Karthik Kalyanaraman Date: Tue, 23 Apr 2024 22:17:49 -0700 Subject: [PATCH 3/4] Fix for schema changes --- .../evaluations/[test_id]/page.tsx | 101 ++++++++++-------- components/shared/llm-view.tsx | 71 ++++++------ components/shared/user-logo.tsx | 7 ++ components/shared/vendor-metadata.tsx | 76 +++++++++++-- public/cohere.png | Bin 0 -> 4811 bytes 5 files changed, 164 insertions(+), 91 deletions(-) create mode 100644 components/shared/user-logo.tsx create mode 100644 public/cohere.png diff --git a/app/(protected)/project/[project_id]/evaluations/[test_id]/page.tsx b/app/(protected)/project/[project_id]/evaluations/[test_id]/page.tsx index 5eabf586..8f0ed58f 100644 --- a/app/(protected)/project/[project_id]/evaluations/[test_id]/page.tsx +++ b/app/(protected)/project/[project_id]/evaluations/[test_id]/page.tsx @@ -2,6 +2,7 @@ import { ScaleType } from "@/components/evaluations/eval-scale-picker"; import { RangeScale } from "@/components/evaluations/range-scale"; +import UserLogo from "@/components/shared/user-logo"; import { VendorLogo } from "@/components/shared/vendor-metadata"; import { Button } from "@/components/ui/button"; import { Skeleton } from "@/components/ui/skeleton"; @@ -315,7 +316,7 @@ export default function Page() { size="md" value={(page / totalPages) * 100} > - + {Math.round((page / totalPages) * 100)}% @@ -348,8 +349,8 @@ export default function Page() { className={cn( "ml-2 text-xs px-1 py-[2px] rounded-md", evaluationsData?.evaluations[0]?.id - ? "bg-green-400" - : "bg-orange-400" + ? "bg-green-400 dark:bg-green-800" + : "bg-orange-400 dark:bg-orange-800" )} > {evaluationsData?.evaluations[0]?.id @@ -474,49 +475,63 @@ function ConversationView({ span }: { span: any }) { if (!prompts && !responses) return

No data found

; return ( -
+
{prompts?.length > 0 && - JSON.parse(prompts).map((prompt: any, i: number) => ( -
-

- {prompt?.role - ? prompt?.role === "function" - ? `${prompt?.role} - ${prompt?.name}` - : prompt?.role - : "Input"} - : - {prompt?.content - ? " (content)" - : prompt?.function_call - ? " (function call)" - : ""} -

{" "} - - {prompt?.content - ? prompt?.content - : prompt?.function_call - ? JSON.stringify(prompt?.function_call) - : "No input found"} - -
- ))} + JSON.parse(prompts).map((prompt: any, i: number) => { + const role = prompt?.role ? prompt?.role?.toLowerCase() : "User"; + const content = prompt?.content + ? JSON.stringify(prompt?.content) + : prompt?.function_call + ? JSON.stringify(prompt?.function_call) + : "No input found"; + return ( +
+
+ {role === "user" ? ( + + ) : ( + + )} +

{role}

+ {role === "system" && ( +

+ Prompt +

+ )} +
+ + {content} + +
+ ); + })} {responses?.length > 0 && - JSON.parse(responses).map((response: any, i: number) => ( -
-
- -

- {response?.message?.role || "Output"}: -

{" "} + JSON.parse(responses).map((response: any, i: number) => { + const role = + response?.role?.toLowerCase() || + response?.message?.role || + "Assistant"; + const content = + JSON.stringify(response?.content) || + JSON.stringify(response?.message?.content) || + JSON.stringify(response?.text) || + "No output found"; + return ( +
+
+ {role === "user" ? ( + + ) : ( + + )} +

{role}

+
+ + {content} +
- - {response?.message?.content || - response?.text || - response?.content || - "No output found"} - -
- ))} + ); + })}
); } diff --git a/components/shared/llm-view.tsx b/components/shared/llm-view.tsx index 5b255b8a..b32de5d4 100644 --- a/components/shared/llm-view.tsx +++ b/components/shared/llm-view.tsx @@ -24,17 +24,7 @@ export const LLMView = ({ className="text-xs bg-muted w-fit p-1 rounded-md leading-6" > - {prompt?.role - ? prompt?.role === "function" - ? `${prompt?.role} - ${prompt?.name}` - : prompt?.role - : "Q"} - : - {prompt?.content - ? " (content)" - : prompt?.function_call - ? " (function call)" - : ""} + {prompt?.role ? prompt?.role?.toLowerCase() : "User"} {" "} {prompt?.content - ? prompt?.content + ? JSON.stringify(prompt?.content) : prompt?.function_call ? JSON.stringify(prompt?.function_call) : ""}

))} -
- - {JSON.parse(responses)[0]?.message?.role || "Assistant"}: - {" "} - {responses?.length > 0 ? ( - 0 && - "underline decoration-red-600 decoration-[3px]" - )} + {responses?.length > 0 && + JSON.parse(responses).map((response: any, i: number) => ( +
- {JSON.parse(responses)[0]?.message?.content || - JSON.parse(responses)[0]?.text || - JSON.parse(responses)[0]?.content || - JSON.parse(responses).message?.content} - - ) : ( - "" - )} - {Evaluate && } -
+ + {response?.role?.toLowerCase() || + response?.message?.role || + "Assistant"} + : + {" "} + 0 && + "underline decoration-red-600 decoration-[3px]" + )} + > + {JSON.stringify(response?.content) || + JSON.stringify(response?.message?.content) || + JSON.stringify(response?.text) || + ""} + + {Evaluate && } +
+ ))}
); }; diff --git a/components/shared/user-logo.tsx b/components/shared/user-logo.tsx new file mode 100644 index 00000000..422b1d70 --- /dev/null +++ b/components/shared/user-logo.tsx @@ -0,0 +1,7 @@ +export default function UserLogo() { + return ( +
+ U +
+ ); +} diff --git a/components/shared/vendor-metadata.tsx b/components/shared/vendor-metadata.tsx index 54a145ee..4508ec0b 100644 --- a/components/shared/vendor-metadata.tsx +++ b/components/shared/vendor-metadata.tsx @@ -1,3 +1,4 @@ +import { cn } from "@/lib/utils"; import { StackIcon } from "@radix-ui/react-icons"; import Image from "next/image"; @@ -37,6 +38,10 @@ export function vendorBadgeColor(vendor: string) { return "bg-indigo-500"; } + if (vendor.includes("cohere")) { + return "bg-red-500"; + } + return "bg-gray-500"; } @@ -73,6 +78,10 @@ export function vendorColor(vendor: string) { return "bg-indigo-200"; } + if (vendor.includes("cohere")) { + return "bg-red-200"; + } + return "bg-gray-800"; } @@ -87,7 +96,13 @@ export function serviceTypeColor(serviceType: string) { return "bg-gray-500"; } -export function VendorLogo({ span }: { span: Span }) { +export function VendorLogo({ + span, + variant = "default", +}: { + span: Span; + variant?: string; +}) { const attributes = span.attributes ? JSON.parse(span.attributes) : {}; let serviceName = ""; if (attributes["langtrace.service.name"]) { @@ -102,7 +117,10 @@ export function VendorLogo({ span }: { span: Span }) { src="/perplexity.png" width={20} height={20} - className={`${color} p-[3px] rounded-sm`} + className={cn( + `${color} p-[3px]`, + variant === "circular" ? "rounded-full" : "rounded-md" + )} /> ); } @@ -115,7 +133,10 @@ export function VendorLogo({ span }: { span: Span }) { src="/openai.svg" width={20} height={20} - className={`${color} p-[3px] rounded-sm`} + className={cn( + `${color} p-[3px]`, + variant === "circular" ? "rounded-full" : "rounded-md" + )} /> ); } @@ -128,7 +149,10 @@ export function VendorLogo({ span }: { span: Span }) { src="/anthropic.png" width={30} height={30} - className="p-[3px] rounded-md" + className={cn( + `${color} p-[3px]`, + variant === "circular" ? "rounded-full" : "rounded-md" + )} /> ); } @@ -141,7 +165,10 @@ export function VendorLogo({ span }: { span: Span }) { src="/pinecone.png" width={20} height={20} - className={`${color} p-[3px] rounded-sm`} + className={cn( + `${color} p-[3px]`, + variant === "circular" ? "rounded-full" : "rounded-md" + )} /> ); } @@ -154,7 +181,10 @@ export function VendorLogo({ span }: { span: Span }) { src="/chroma.png" width={25} height={25} - className={`${color} p-[3px] rounded-sm`} + className={cn( + `${color} p-[3px]`, + variant === "circular" ? "rounded-full" : "rounded-md" + )} /> ); } @@ -167,7 +197,10 @@ export function VendorLogo({ span }: { span: Span }) { src="/langchain.svg" width={30} height={30} - className={`${color} p-[3px] rounded-sm`} + className={cn( + `${color} p-[3px]`, + variant === "circular" ? "rounded-full" : "rounded-md" + )} /> ); } @@ -180,15 +213,38 @@ export function VendorLogo({ span }: { span: Span }) { src="/llamaindex.svg" width={60} height={80} - className={`${color} p-[3px] rounded-sm`} + className={cn( + `${color} p-[3px]`, + variant === "circular" ? "rounded-full" : "rounded-md" + )} + /> + ); + } + + if (span.name.includes("cohere") || serviceName.includes("cohere")) { + const color = vendorColor("cohere"); + return ( + Cohere Logo ); } - const color = vendorColor("langtrace"); return (
- +
); } diff --git a/public/cohere.png b/public/cohere.png new file mode 100644 index 0000000000000000000000000000000000000000..db1cc73fbc12e8f4c219ea750be6cb956532577c GIT binary patch literal 4811 zcmZWsc{r49)Sof7tb@pIY+1r!>@l{H89T|+Pz+-UGnOeP%9?#mBpOSWB8n`f>^q5& zrLxPOh-A+-+BOI`=uxIp=rIbN|l$&mCiCqR&XjO$Pt~7*R-s1z9VQMTM4{ zyuWz;IT-+8&~(?;HACs@Ld<;eF7BRK000@2W=&&rv6~|seK%Rl4M>75Q;=&+HLRk4?sxZl{8F6+RK5`az^0U zc8iBHotG>gN_HMN!+qqWqR{{|iy7~>Wx$gf-R$gs<0$TuBk*}TB84E}8X@|YuW!*N z#3Xc#dp{bIz5=rdevdlNa#<-q8G zGUxI`#|fOPF=Hx*J^CP`m?Z`5p>wF+l)9%IZy_%t_}w@7LrB)!Fh!6W`23~{&=Jg| z#VgXANNc@O>>dVDYV*F6OBJSmgSYK97`m`X-60vI6!*A89Fw>0j^Vi2e04e+>r+_4 zZ}4LF0o~CkS5S?Pz(wiaXv2sMiV9)_M34e=DVoo>64QDb?nDo>VbO(SQ1J@Ee$h_5 zEe^r8@bsLsOtI#>_rH8$dUxjYLqP|9@r}^B_*Y=!t#S?-{dCQT^do&|;PhbMDzZ6zWN|*qtjn{OTZqr>ii;-7dvQWhc{|DX3U>#i(ftJ=xdC02NPzoVrWe7E9VGcX$)1$;?bUH_E8cKM31gGTPOf9{Q;BtX2GVWqu_+ zU~oO|aC`gg<)0H;gv*V7^uGX22VbVArw7*UmQaA7Z(2K#yZrkyftq|h06UeIwCJsQ zUKtJWG+Kud?tZh0HUsFPBH%hrT^AxiOPL2@Z3`8V<#NdZj@EJ-(Ns1CnK2kN)0J~r zh8R1X8$0zg1e6Qp1wC$7_2#My=b7fW)0mj1TB2ERa;4Np(z53X-i(&RgpLXJ(Sp8f zQ>1u{P@{A#k_m`tWU_4+MZsy?JC+c1GPkp)FQsaN9YQVne3x*+8Dc_^j+`acunwcS zAwi)>>wz05ca(|F(15#};A^mbWP1Ma60VA0BjL&gd@gB;o&W%4$QOk_aQMjya&kADSDUH@pgNh$L|G@B2%N$1)O9aHn1j|`kKCmh;-Zi5t{F&TEX zRbb|5SD7NsL91LifORb5djz3{pd*(TUrzGQ@aBLUPDJ=4gv_Jd^axS?7*@GdmDE@F zUWLB89}qjH{Z)u587ZQ1m#;hPG-@_^6WLy{T`*CQTJW?0H%KFj6?GDIErb+0i{@FR zSUeb%DP%LSPRo|Kb@pkhbXWElqk(5e{kz`H7N}jhgDIPZ`>K0l^ z7vKKie2xD)S9Rpg$o=Gf?X;qm+#aj>;=W5|mSxuUCHoc$r7n3LmRY4%Pppb=n61TL z&QY_SupkxondjtRDZY(3O5YEmHf}`;TL|kr6?e!a#Bpn6iWnQd(=yIp(0Nyu>8kL? zjq_c6$0HwidbiJRuI>|;5z^<8GD^sqg3x^%n3?md$&Gl& zoRq|r9P^?9?tHcj^&JXM0#2ddx#v`F~R5UWN~IhWFU=dGO8W~DOJlS zC`-#vc)CFVQ8lj&k;P%6W$k;PAuEAr+-1!D%6T0wG-oZ(yh{C(&MCfqykF` zWn)%%tg5Yc%8ExCMutkCmOcLr_Tp5sRpRmd<>%;Pbf~Pe@s$CS16Er+ABa zl6i~ya6azi(=piE)|$tffa;P;#QD>z3MzHHUT`(ykDh@={a`=B_1+uf3jzJ8A&C#g zUB%tY)s^v<0=k>nR?UtJcc?$`xo5`uUsM zz}UdF1L;T(;n<3MKF#6NGwX7ra~Ylbrpo>5{h;h%;>SQNacy&j%R@0;^Ty{f8T>_j z;tt2UiNI^q*nXEBldEi36Yy?5P`#u0}jAXZ@773LEnq$BxU71JlJklvXVP z9AI8E*7qDwxHtuDSaxN*6vQ>2C_T{}Qw(s~C~uu4KnMsldc{Mh&<9>VSoVr6oEN3GPm`^Xwkoe+eb>lF=BcMJA|4Z^$rNVmEC3lVG$>pa*fv_N0kNiHRl=hwJOD@QT zqTU_b6x;JJs4LkjcNWCw3%>BOT%)UukiM8u#9_1s{umRhM;cOpTt*s!GO^+N^?b$V zl@{Ea1@^3Bs=u7+&JW5t>^Wo}Vk;7mdM^&xRXBH4w&b=!`^^w_rH2DG}p zqxgL-a_^xYu3> zdmDQTyR!BEv+-I3r%N*>&xYr$MxWhc-*CHq zAIi?ft}Jw1*8{$gJSNm}unqT(Bd8`979JVdMO--vy~O|g|fuIOvPy}d%J zFFa_1cQ-5^T>Lqu4!)7Q2bpu25RB8k6y+XexRSEZwSC0M#Yjp(CGhF%Bq=Bp75U&R z-h@uMRHwAg>LjS9wra&j+jMtJD(Erd@xIC9oNw2%2ZPpocVVOGN9a#|k)(QWnWgau zTfJLi(15EdHwLz5Qw-w`8ShWXjJ|wb!~bhyd#BY4=e2D;{#>CZuuA!Q{F;i-@XpGz z2XM5{-t}r@knrq<&Ud?6FY(58!^ABAH;X1eN!I>b>$e+}*Woj_yFuGDSY}3kt7FHl z&PkJ(eRD|^{MGQpTS3p`K5x&WzNJDZ=yNP`(lubWt%l~t{YsUeygb^ASe$?&pzs~` z4;nY(7$L}NaURzopnp*u;*Wr!Oz1$tgMg5M> ztsGWOjRW-~`enmC>#XO-Q!Nb>0=-iTpJSa&VrVLn_J`N-Z9KNZgYHINEKA_qjK1%`Cqk{B-a-BLDUgp(jdR~=K z6dI@TOgzlYOr`sk(7p3e^UKi|7xI#QQ~zQxa7r>LTzE7pk^G{F#oC};jEw=3WSbU1 z3FHP)ku4xu;6R=~_649g0QB2W0RTj~11SH^F(K=dMShh0#{8o}cOwBbWDhG@0&*$- zWGm!?{@5zy7(n}?E(%4~7oB{uSe&05o)GyVQIqUA<&CuU0{}QgPXvgv5ZNHt|KM(E zL$EP6Qggz4$v8UWF<6-ZFYl8&0QCShvgw5-I6?xvJaK+%0UAQTGt|iTi5n^e`JF=W z&=9gQHiPKmeX$TF8Ce-wAvhfb0#WyMc2ToHT=*AG{?ZU~BM`jRpiqB*e;I$64Bpoj zDyOQd3YC?I%F9cWGo<~9ID%tpCbQT2Z8l-@^$wnxZ`nlyNdE05vka$G zhyIxuoNk*5>_#3UuRFrjlB~&B?Dyg&@8V=R(PRPQ<|{Uu$yyDC(6)4=!W{0;4+Td$ zQgZ5RD0DgbDMV5eDdplj-n%gjs8U`?<-HklwSh>QVFib~fkOtCR**BRB_8r2vb2Z; zs+31*hdRJ78--aT=Ix+eJ6@_EV2Ny@bKRlpFBqbiskADF%`Y3@yDkzKJ((T47dDn5 z^ig+GLQeMlk_nzv8Iem8A-d&aXcp+rUWBuA$jT8i-Izn^LSw&K5snD+7l(C0eCKnM zh0hAG@acv(>slt{g-S6vEN7P7Y0(Od&iF#TsWO0KvV@rB+k)u5EZj0JOF?N-$nOj& zIm9}K=L?G&Poi0UIR6krOf~t!O_B4y8WdZcDE>!SDf1PU7wVLQh_yO|bW-w~;I)9* zPuU?vYDDV2FUg8`*)G0P-`pIxE+4ph^)M1Gi0gXSB;8H7$oBSx)#KxE)&l~WWRnvX zoqlsjP1iJNyxQ~{I#t&PXj#&-woeuO1dRqF^26^hHpthhA2Zr>-htw}+99~M1fj^J zR%CJ1dq(}Ar)5k-etwFnUqKz@Wt~u)BY#+C^xwEs-k}JPWEiROn^!itw+<@;h56S|hh zzM5j`FxkWXm{DO_lnWn>nxkJ__I@TRkv!q`>Pk*6Jteu2b`1)<`0x)Fm1btt9JJ$2 zVL$`Kje9K!YY#QChLV(o2}#<9)8^ z7&cjSEv(!o@KM*r6$#8HQ?Jfl^&iLWPLWgR)8~GiuFMt%#RaPLdY;a-GiBj{$-YHy zf{ob3_m1B9WY0{czF~+j^<*&W`NUwPw!mOC7%MAk<8JnJW`6IvgKAKkwD^TrqBdLs zA|GiNxCn7wo8Z@i;W3&kdCjMJ#>VBjb;oqDvM~uA?;T Date: Tue, 23 Apr 2024 23:59:45 -0700 Subject: [PATCH 4/4] Render tool calling --- .../evaluations/[test_id]/page.tsx | 30 +++-- components/evaluations/evaluation-row.tsx | 14 +-- components/project/traces/trace-row.tsx | 14 +-- components/shared/hover-cell.tsx | 49 +++++++- components/shared/llm-view.tsx | 114 +++++++++--------- lib/pii.ts | 6 +- lib/utils.ts | 10 ++ package-lock.json | 27 +++++ package.json | 1 + 9 files changed, 170 insertions(+), 95 deletions(-) diff --git a/app/(protected)/project/[project_id]/evaluations/[test_id]/page.tsx b/app/(protected)/project/[project_id]/evaluations/[test_id]/page.tsx index 8f0ed58f..da6bb2e5 100644 --- a/app/(protected)/project/[project_id]/evaluations/[test_id]/page.tsx +++ b/app/(protected)/project/[project_id]/evaluations/[test_id]/page.tsx @@ -11,6 +11,7 @@ import { cn, extractSystemPromptFromLlmInputs, formatDateTime, + safeStringify, } from "@/lib/utils"; import { Cross1Icon, EnterIcon } from "@radix-ui/react-icons"; import { ProgressCircle } from "@tremor/react"; @@ -24,7 +25,6 @@ import { } from "lucide-react"; import { useParams, useRouter, useSearchParams } from "next/navigation"; import { useEffect, useState } from "react"; -import Markdown from "react-markdown"; import { useQuery, useQueryClient } from "react-query"; import { toast } from "sonner"; @@ -480,9 +480,9 @@ function ConversationView({ span }: { span: any }) { JSON.parse(prompts).map((prompt: any, i: number) => { const role = prompt?.role ? prompt?.role?.toLowerCase() : "User"; const content = prompt?.content - ? JSON.stringify(prompt?.content) + ? safeStringify(prompt?.content) : prompt?.function_call - ? JSON.stringify(prompt?.function_call) + ? safeStringify(prompt?.function_call) : "No input found"; return (
@@ -499,9 +499,12 @@ function ConversationView({ span }: { span: any }) {

)}
- - {content} - +
); })} @@ -512,9 +515,9 @@ function ConversationView({ span }: { span: any }) { response?.message?.role || "Assistant"; const content = - JSON.stringify(response?.content) || - JSON.stringify(response?.message?.content) || - JSON.stringify(response?.text) || + safeStringify(response?.content) || + safeStringify(response?.message?.content) || + safeStringify(response?.text) || "No output found"; return (
@@ -526,9 +529,12 @@ function ConversationView({ span }: { span: any }) { )}

{role}

- - {content} - +
); })} diff --git a/components/evaluations/evaluation-row.tsx b/components/evaluations/evaluation-row.tsx index 31772c0a..f303f5cc 100644 --- a/components/evaluations/evaluation-row.tsx +++ b/components/evaluations/evaluation-row.tsx @@ -215,18 +215,12 @@ export default function EvaluationRow({

{model}

0 ? JSON.parse(prompts)[0]?.content : ""} + className="flex items-center text-xs h-10 truncate overflow-y-scroll font-semibold col-span-2" + values={prompts?.length > 0 ? JSON.parse(prompts) : []} /> 0 - ? JSON.parse(responses)[0]?.message?.content || - JSON.parse(responses)[0]?.text || - JSON.parse(responses)[0]?.content - : "" - } + className="flex items-center text-xs h-10 truncate overflow-y-scroll font-semibold col-span-2" + values={responses?.length > 0 ? JSON.parse(responses) : []} />

{cost.total.toFixed(6) !== "0.000000" diff --git a/components/project/traces/trace-row.tsx b/components/project/traces/trace-row.tsx index a340926d..64922495 100644 --- a/components/project/traces/trace-row.tsx +++ b/components/project/traces/trace-row.tsx @@ -126,18 +126,12 @@ export const TraceRow = ({

{model}

0 ? JSON.parse(prompts)[0]?.content : ""} - className="max-w-fit text-xs h-10 truncate overflow-y-scroll font-semibold col-span-2" + values={prompts?.length > 0 ? JSON.parse(prompts) : []} + className="flex items-center max-w-fit text-xs h-10 truncate overflow-y-scroll font-semibold col-span-2" /> 0 - ? JSON.parse(responses)[0]?.message?.content || - JSON.parse(responses)[0]?.text || - JSON.parse(responses)[0]?.content - : "" - } - className="max-w-fit text-xs h-10 truncate overflow-y-scroll font-semibold col-span-2" + values={responses?.length > 0 ? JSON.parse(responses) : []} + className="flex items-center max-w-fit text-xs h-10 truncate overflow-y-scroll font-semibold col-span-2" />

{userId}

diff --git a/components/shared/hover-cell.tsx b/components/shared/hover-cell.tsx index 38e87d6c..4f9132fa 100644 --- a/components/shared/hover-cell.tsx +++ b/components/shared/hover-cell.tsx @@ -3,22 +3,61 @@ import { HoverCardContent, HoverCardTrigger, } from "@/components/ui/hover-card"; -import Markdown from "react-markdown"; +import { safeStringify } from "@/lib/utils"; export function HoverCell({ - value, + values, className, }: { - value: string; + values: any[]; className?: string; }) { + const contents = values.map((value, i) => { + const role = value?.role + ? value?.role?.toLowerCase() + : value?.message?.role + ? value?.message?.role + : "User"; + const content = value?.content + ? safeStringify(value?.content) + : value?.function_call + ? safeStringify(value?.function_call) + : value?.message?.content + ? safeStringify(value?.message?.content) + : value?.text + ? safeStringify(value?.text) + : ""; + return { role, content }; + }); + + if (contents.length === 0) { + return null; + } + return ( - +

- {value} +
+ {contents.map((item, i) => ( +
+

+ {item.role} +

+
+
+ ))} +
); diff --git a/components/shared/llm-view.tsx b/components/shared/llm-view.tsx index b32de5d4..be76166c 100644 --- a/components/shared/llm-view.tsx +++ b/components/shared/llm-view.tsx @@ -1,8 +1,7 @@ "use client"; import detectPII from "@/lib/pii"; -import { cn } from "@/lib/utils"; -import Markdown from "react-markdown"; +import { cn, safeStringify } from "@/lib/utils"; export const LLMView = ({ prompts, @@ -18,61 +17,68 @@ export const LLMView = ({ return (
{prompts?.length > 0 && - JSON.parse(prompts).map((prompt: any, i: number) => ( -

- - {prompt?.role ? prompt?.role?.toLowerCase() : "User"} - {" "} - 0 && - "underline decoration-red-600 decoration-[3px]" - )} + JSON.parse(prompts).map((prompt: any, i: number) => { + const role = prompt?.role ? prompt?.role?.toLowerCase() : "User"; + const content = prompt?.content + ? prompt?.content + : prompt?.function_call + ? prompt?.function_call + : ""; + return ( +

- {prompt?.content - ? JSON.stringify(prompt?.content) - : prompt?.function_call - ? JSON.stringify(prompt?.function_call) - : ""} - -

- ))} + + {role} + {" "} +
0 && + "underline decoration-red-600 decoration-[3px]" + )} + dangerouslySetInnerHTML={{ __html: safeStringify(content) }} + /> +
+ ); + })} {responses?.length > 0 && - JSON.parse(responses).map((response: any, i: number) => ( -
- - {response?.role?.toLowerCase() || - response?.message?.role || - "Assistant"} - : - {" "} - 0 && - "underline decoration-red-600 decoration-[3px]" - )} + JSON.parse(responses).map((response: any, i: number) => { + const role = + response?.role?.toLowerCase() || + response?.message?.role || + "Assistant"; + const content = + response?.content || + response?.message?.content || + response?.text || + ""; + + return ( +
- {JSON.stringify(response?.content) || - JSON.stringify(response?.message?.content) || - JSON.stringify(response?.text) || - ""} - - {Evaluate && } -
- ))} + + {role} + +
0 && + "underline decoration-red-600 decoration-[3px]" + )} + dangerouslySetInnerHTML={{ __html: safeStringify(content) }} + /> + {Evaluate && } +
+ ); + })}
); }; diff --git a/lib/pii.ts b/lib/pii.ts index 1d631c9a..4577e1eb 100644 --- a/lib/pii.ts +++ b/lib/pii.ts @@ -1,8 +1,5 @@ export default function detectPII(text: string): string[] { try { - JSON.parse(text); - return []; - } catch (e) { // Define regular expressions for various types of PII const patterns = { phoneNumber: /\b\d{3}[-.]?\d{3}[-.]?\d{4}\b/g, // Simplified phone number pattern (US-centric) @@ -24,7 +21,8 @@ export default function detectPII(text: string): string[] { foundPII.push(...matches.map((match) => `${type}: ${match}`)); } } - return foundPII; + } catch (error) { + return []; } } diff --git a/lib/utils.ts b/lib/utils.ts index 3935dfe9..9c04bda9 100644 --- a/lib/utils.ts +++ b/lib/utils.ts @@ -3,6 +3,7 @@ import { clsx, type ClassValue } from "clsx"; import { createHash, randomBytes } from "crypto"; import { TiktokenEncoding, getEncoding } from "js-tiktoken"; import { NextResponse } from "next/server"; +import { prettyPrintJson } from "pretty-print-json"; import { twMerge } from "tailwind-merge"; import { Span } from "./clients/scale3_clickhouse/models/span"; import { @@ -405,3 +406,12 @@ export const getChartColor = (value: number) => { return "green"; } }; + +export function safeStringify(value: any): string { + // Check if the value is already a string + if (typeof value === "string") { + return value; + } + // If it's not a string, stringify it + return prettyPrintJson.toHtml(value); +} diff --git a/package-lock.json b/package-lock.json index d5bd2206..4568a4a0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -53,6 +53,7 @@ "f": "^1.4.0", "framer-motion": "^11.0.5", "fs": "^0.0.1-security", + "interweave": "^13.1.0", "js-tiktoken": "^1.0.10", "langchain": "^0.1.20", "lucide-react": "^0.323.0", @@ -63,6 +64,7 @@ "next-themes": "^0.2.1", "openai": "^4.26.0", "pdf-parse": "^1.1.1", + "pretty-print-json": "^3.0.0", "prism": "^4.1.2", "prismjs": "^1.29.0", "react": "^18", @@ -5727,6 +5729,11 @@ "node": ">=6" } }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -7123,6 +7130,21 @@ "node": ">=12" } }, + "node_modules/interweave": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/interweave/-/interweave-13.1.0.tgz", + "integrity": "sha512-JIDq0+2NYg0cgL7AB26fBcV0yZdiJvPDBp+aF6k8gq6Cr1kH5Gd2/Xqn7j8z+TGb8jCWZn739jzalCz+nPYwcA==", + "dependencies": { + "escape-html": "^1.0.3" + }, + "funding": { + "type": "ko-fi", + "url": "https://ko-fi.com/milesjohnson" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/invariant": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", @@ -10448,6 +10470,11 @@ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz", "integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==" }, + "node_modules/pretty-print-json": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pretty-print-json/-/pretty-print-json-3.0.0.tgz", + "integrity": "sha512-1993HBv7RNqoPNgaajOK3RLm544t3jongECDFw6Y5faYiBeg2tg62jDT1EDmXRRDn296ykH49A8tphkQBHUSTA==" + }, "node_modules/prism": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/prism/-/prism-4.1.2.tgz", diff --git a/package.json b/package.json index 923018f0..8d8ba00f 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "next-themes": "^0.2.1", "openai": "^4.26.0", "pdf-parse": "^1.1.1", + "pretty-print-json": "^3.0.0", "prism": "^4.1.2", "prismjs": "^1.29.0", "react": "^18",