diff --git a/.github/resources/slack-payloads/slack-message-template.json b/.github/resources/slack-payloads/slack-message-template.json new file mode 100644 index 00000000..ea175a3f --- /dev/null +++ b/.github/resources/slack-payloads/slack-message-template.json @@ -0,0 +1,22 @@ +{ + "attachments": [ + { + "pretext": "{{ env.PRE_TEXT_MESSAGE }}", + "fallback": "{{ env.FALLBACK_MESSAGE }}", + "color": "{{ env.COLOR }}", + "author_name": "{{ github.workflow }}", + "author_link": "{{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }}", + "title": "{{ env.GITHUB_REPOSITORY }}", + "title_link": "{{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}", + "fields": [ + { + "title": "Release Tag", + "short": true, + "value": "{{ env.RELEASETAG }}" + } + ], + "footer": "deployed by: {{ github.actor }}", + "footer_icon": "https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png" + } + ] +} diff --git a/.github/workflows/release_langtrace.yaml b/.github/workflows/release_langtrace.yaml index 45a914b5..cf7fcce7 100644 --- a/.github/workflows/release_langtrace.yaml +++ b/.github/workflows/release_langtrace.yaml @@ -64,3 +64,45 @@ jobs: tag: ${{ needs.generate-version.outputs.new_version }} generateReleaseNotes: true draft: false + + post-release: + name: Post Release Actions + + runs-on: ubuntu-latest + if: ${{ always() }} + needs: + - generate-version + - build-docker + - publish + + steps: + - name: Checkout main branch + uses: actions/checkout@v4.1.7 + with: + ref: main + + - name: Slack - Success Message + uses: DSdatsme/slack-github-action@env_support + if: ${{ success() && needs.build-docker.result == 'success' && needs.publish.result == 'success' }} + with: + channel-id: ${{ vars.SLACK_CHANNEL_ID }} + payload-file-path: ./.github/resources/slack-payloads/slack-message-template.json + env: + SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} + RELEASETAG: ${{ needs.generate-version.outputs.new_version }} + PRE_TEXT_MESSAGE: "Workflow Passed! :successkid:" + FALLBACK_MESSAGE: "Workflow Passed!" + COLOR: "good" + + - name: Slack - Failure Message + uses: DSdatsme/slack-github-action@env_support + if: ${{ failure() || needs.build-docker.result != 'success' || needs.publish.result != 'success' }} + with: + channel-id: ${{ vars.SLACK_CHANNEL_ID }} + payload-file-path: ./.github/resources/slack-payloads/slack-message-template.json + env: + SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} + RELEASETAG: ${{ needs.generate-version.outputs.new_version }} + PRE_TEXT_MESSAGE: " Workflow Failed! :x:" + FALLBACK_MESSAGE: "Workflow Failed!" + COLOR: "danger" diff --git a/Dockerfile b/Dockerfile index c7cdf011..a40edcb3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # Debian based node 21.6 image -FROM node:21.6-bookworm as development +FROM node:21.6-bookworm AS development LABEL maintainer="Langtrace AI " LABEL version="1.0" @@ -20,14 +20,14 @@ CMD [ "/bin/sh", "-c", "npm run dev" ] # Intermediate image for building the application -FROM development as builder +FROM development AS builder WORKDIR /app RUN NEXT_PUBLIC_ENABLE_ADMIN_LOGIN=true npm run build # Final release image -FROM node:21.6-bookworm as production +FROM node:21.6-bookworm AS production WORKDIR /app diff --git a/README.md b/README.md index 4cffcaa1..9745417d 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ Langtrace.init({ api_key: }) OR ```typescript -import * as Langtrace from "@langtrase/typescript-sdk"; // Must precede any llm module imports +import * as Langtrace from '@langtrase/typescript-sdk'; // Must precede any llm module imports LangTrace.init(); // LANGTRACE_API_KEY as an ENVIRONMENT variable ``` @@ -131,14 +131,16 @@ Langtrace automatically captures traces from the following vendors: | Anthropic | LLM | :white_check_mark: | :white_check_mark: | | Azure OpenAI | LLM | :white_check_mark: | :white_check_mark: | | Cohere | LLM | :white_check_mark: | :white_check_mark: | -| Groq | LLM | :x: | :white_check_mark: | +| Groq | LLM | :white_check_mark: | :white_check_mark: | | Perplexity | LLM | :white_check_mark: | :white_check_mark: | +| Gemini | LLM | :x: | :white_check_mark: | | Langchain | Framework | :x: | :white_check_mark: | | LlamaIndex | Framework | :white_check_mark: | :white_check_mark: | | Langgraph | Framework | :x: | :white_check_mark: | | DSPy | Framework | :x: | :white_check_mark: | | CrewAI | Framework | :x: | :white_check_mark: | | Ollama | Framework | :x: | :white_check_mark: | +| VertexAI | Framework | :x: | :white_check_mark: | | Pinecone | Vector Database | :white_check_mark: | :white_check_mark: | | ChromaDB | Vector Database | :white_check_mark: | :white_check_mark: | | QDrant | Vector Database | :white_check_mark: | :white_check_mark: | diff --git a/app/(protected)/project/[project_id]/annotations/page-client.tsx b/app/(protected)/project/[project_id]/annotations/page-client.tsx index 8632f83a..35191363 100644 --- a/app/(protected)/project/[project_id]/annotations/page-client.tsx +++ b/app/(protected)/project/[project_id]/annotations/page-client.tsx @@ -1,11 +1,11 @@ "use client"; +import { ChartTabs } from "@/components/annotations/chart-tabs"; import { CreateTest } from "@/components/annotations/create-test"; import { EditTest } from "@/components/annotations/edit-test"; import EvaluationTable, { EvaluationTableSkeleton, } from "@/components/annotations/evaluation-table"; -import { EvalChart } from "@/components/charts/eval-chart"; import LargeChartSkeleton from "@/components/charts/large-chart-skeleton"; import { AddtoDataset } from "@/components/shared/add-to-dataset"; import { Button } from "@/components/ui/button"; @@ -85,7 +85,7 @@ export default function PageClient({ email }: { email: string }) { ) : tests?.tests?.length > 0 ? (
- +
([]); const [selectedPrompt, setSelectedPrompt] = useState(); + const [prettyJson, setPrettyJson] = useState(false); const [createDialogOpen, setCreateDialogOpen] = useState(false); const [live, setLive] = useState(false); const queryClient = useQueryClient(); @@ -50,6 +51,15 @@ export default function Page() { }, }); + function isJsonString(str: string) { + try { + JSON.parse(str); + return true; + } catch (e) { + return false; + } + } + if (promptsLoading) return ; if (!selectedPrompt) @@ -217,10 +227,25 @@ export default function Page() {
-

- {selectedPrompt.value} -

+
+ + +
+
+ {prettyJson && isJsonString(selectedPrompt.value) ? ( +
+                    {JSON.stringify(JSON.parse(selectedPrompt.value), null, 2)}
+                  
+ ) : ( + selectedPrompt.value + )} +
+
diff --git a/app/api/metrics/score/route.ts b/app/api/metrics/score/route.ts index 9f4bb20b..46ba7bc8 100644 --- a/app/api/metrics/score/route.ts +++ b/app/api/metrics/score/route.ts @@ -88,31 +88,56 @@ export async function POST(req: NextRequest) { } if (!dateScoreMap[date][`${testId}-${evaluation.Test?.name}`]) { - dateScoreMap[date][`${testId}-${evaluation.Test?.name}`] = 0; + dateScoreMap[date][`${testId}-${evaluation.Test?.name}`] = [0, 0]; } - dateScoreMap[date][`${testId}-${evaluation.Test?.name}`] += - evaluation.ltUserScore || 0; + const total = + dateScoreMap[date][`${testId}-${evaluation.Test?.name}`][0] + + evaluation.ltUserScore || 0; + dateScoreMap[date][`${testId}-${evaluation.Test?.name}`][0] = total; + dateScoreMap[date][`${testId}-${evaluation.Test?.name}`][1] += 1; }); } - const chartData = Object.entries(dateScoreMap).map( + const metricsChartData = Object.entries(dateScoreMap).map( ([date, scoresByTestId]) => { const entry: any = { date }; - Object.entries(scoresByTestId as any).forEach(([testId, score]) => { - entry[testId] = score; - }); + Object.entries(scoresByTestId as any).forEach( + ([testId, scores]: any) => { + entry[testId] = scores[0]; + } + ); return entry; } ); - chartData.sort( + // calculate score % test wise and not date wise + // const scoresChartData: any = { + // "test-1": 10, + // }; + const scoresChartData: any = {}; + Object.entries(dateScoreMap).map(([date, scoresByTestId]) => { + Object.entries(scoresByTestId as any).forEach(([testId, scores]: any) => { + if (!scoresChartData[testId]) { + scoresChartData[testId] = 0; + } + if (scores[1] === 0) { + scoresChartData[testId] = 0; + } + scoresChartData[testId] = scores[0] / scores[1]; + }); + }); + + metricsChartData.sort( (a, b) => new Date(a.date as string).getTime() - new Date(b.date as string).getTime() ); - return NextResponse.json(chartData); + return NextResponse.json({ + metrics: metricsChartData, + scores: scoresChartData, + }); } catch (error) { return NextResponse.json( { diff --git a/app/api/traces/route.ts b/app/api/traces/route.ts index 64034c71..2280c7ec 100644 --- a/app/api/traces/route.ts +++ b/app/api/traces/route.ts @@ -11,6 +11,8 @@ export async function POST(req: NextRequest) { const session = await getServerSession(authOptions); const apiKey = req.headers.get("x-api-key"); const { page, pageSize, projectId, filters } = await req.json(); + + // check if user is logged in or has an api key if (!session || !session.user) { if (apiKey) { const project = await prisma.project.findFirst({ @@ -37,6 +39,40 @@ export async function POST(req: NextRequest) { } } + // check if user has access to the project + const email = session?.user?.email as string; + const user = await prisma.user.findUnique({ + where: { + email, + }, + }); + + if (!user) { + return NextResponse.json( + { + message: "user not found", + }, + { status: 404 } + ); + } + + const project = await prisma.project.findFirst({ + where: { + id: projectId, + teamId: user.teamId, + }, + }); + + if (!project) { + return NextResponse.json( + { + message: "User does not have access to this project", + }, + { status: 403 } + ); + } + + // get traces const traceService = new TraceService(); const traces = await traceService.GetTracesInProjectPaginated( projectId, diff --git a/components/annotations/chart-tabs.tsx b/components/annotations/chart-tabs.tsx new file mode 100644 index 00000000..08c7d607 --- /dev/null +++ b/components/annotations/chart-tabs.tsx @@ -0,0 +1,112 @@ +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { Test } from "@prisma/client"; +import { ProgressCircle } from "@tremor/react"; +import { useState } from "react"; +import { useQuery } from "react-query"; +import { EvalChart } from "../charts/eval-chart"; +import { timeRanges } from "../shared/day-filter"; + +export function ChartTabs({ + projectId, + tests, +}: { + projectId: string; + tests: Test[]; +}) { + const [lastNHours, setLastNHours] = useState(timeRanges[0].value); + + const { data: chartData, isLoading } = useQuery({ + queryKey: ["fetch-score", projectId, lastNHours], + queryFn: async () => { + const filters = { + filters: [ + { + operation: "OR", + filters: [ + { + key: "llm.prompts", + operation: "NOT_EQUALS", + value: "", + type: "attribute", + }, + { + key: "name", + operation: "EQUALS", + value: "gen_ai.content.prompt", + type: "event", + }, + ], + }, + { + key: "status_code", + operation: "EQUALS", + value: "OK", + type: "property", + }, + ], + operation: "AND", + }; + const response = await fetch("/api/metrics/score", { + method: "POST", + body: JSON.stringify({ + projectId, + testIds: tests.map((test) => test.id), + lastNHours, + filters, + }), + }); + const result = await response.json(); + return result; + }, + }); + + if (isLoading || !chartData) { + return
Loading
; + } else { + return ( + + + Overall Metrics + Total Score + + + + + + {Object.keys(chartData?.scores).length > 0 ? ( +
+ {Object.keys(chartData?.scores).map( + (testId: string, index: number) => { + return ( +
+ + + {(chartData?.scores[testId] || 0) * 100}% + + + + {tests.find((test) => testId.includes(test.id))?.name} + +
+ ); + } + )} +
+ ) : ( +
+

No data available

+
+ )} +
+
+ ); + } +} diff --git a/components/charts/eval-chart.tsx b/components/charts/eval-chart.tsx index 9a4b7852..68c36e16 100644 --- a/components/charts/eval-chart.tsx +++ b/components/charts/eval-chart.tsx @@ -10,37 +10,6 @@ import DayFilter, { timeRanges } from "../shared/day-filter"; import { Info } from "../shared/info"; import LargeChartLoading from "./large-chart-skeleton"; -const data = [ - { - testId: "clx2ua1ag002xpctlwyupmoaz", - overall: 100, - perday: [ - { - date: "2024-05-22", - score: 100, - }, - { - date: "2024-05-21", - score: 100, - }, - ], - }, - { - testId: "clx2ua1ag112xpctlwyupmoaz", - overall: 50, - perday: [ - { - date: "2024-05-22", - score: 50, - }, - { - date: "2024-05-18", - score: 50, - }, - ], - }, -]; - export function EvalChart({ projectId, tests, @@ -114,7 +83,7 @@ export function EvalChart({
`${test.id}-${test.name}`)} colors={["purple", "blue", "red", "green", "orange", "black"]} diff --git a/components/project/traces/trace-filter.tsx b/components/project/traces/trace-filter.tsx index 78ca1e78..234dcaa3 100644 --- a/components/project/traces/trace-filter.tsx +++ b/components/project/traces/trace-filter.tsx @@ -1,5 +1,6 @@ -"use client"; - +import { Info } from "@/components/shared/info"; +import { PromptCombobox } from "@/components/shared/prompt-combobox"; +import { UserCombobox } from "@/components/shared/user-combobox"; import { Button } from "@/components/ui/button"; import { Command, @@ -23,13 +24,18 @@ import { PopoverTrigger, } from "@/components/ui/popover"; import { ScrollArea } from "@/components/ui/scroll-area"; +import { Separator } from "@/components/ui/separator"; +import { + HOW_TO_PROMPT_FETCHING, + HOW_TO_USER_ID, + OTEL_GENAI_ATTRIBUTES, +} from "@/lib/constants"; +import { PropertyFilter } from "@/lib/services/query_builder_service"; +import { SpanAttributes } from "@/lib/ts_sdk_constants"; import ClearIcon from "@mui/icons-material/Clear"; -import { Check, ChevronsUpDown } from "lucide-react"; +import { Check, ChevronsUpDown, MinusCircle, PlusCircle } from "lucide-react"; +import Link from "next/link"; import { useEffect, useState } from "react"; - -import { PromptCombobox } from "@/components/shared/prompt-combobox"; -import { UserCombobox } from "@/components/shared/user-combobox"; -import { SpanAttributes } from "@/lib/ts_sdk_constants"; import VendorDropdown from "./vendor-dropdown"; export default function FilterDialog({ @@ -39,12 +45,11 @@ export default function FilterDialog({ }: { open: boolean; onClose: () => void; - onApplyFilters: (filters: any) => void; + onApplyFilters: (filters: PropertyFilter[]) => void; }) { - const [selectedFilters, setSelectedFilters] = useState([]); - const [advancedFilters, setAdvancedFilters] = useState([]); - const [selectedUserId, setSelectedUserId] = useState(""); - const [selectedPromptId, setSelectedPromptId] = useState(""); + const [advancedFilters, setAdvancedFilters] = useState([]); + const [userId, setUserId] = useState(""); + const [promptId, setPromptId] = useState(""); useEffect(() => { if (!open) { @@ -59,173 +64,233 @@ export default function FilterDialog({ } }, [open]); - const handleFilterChange = (value: any) => { - setSelectedFilters((prev) => { - if (prev.includes(value)) { - return prev.filter((e) => e !== value); + const handleFilterChange = ( + filter: PropertyFilter, + filterIndex: number = -1, + remove: boolean = true + ) => { + setAdvancedFilters((prev) => { + // remove the filter + if (filterIndex !== -1) { + if (remove) { + return prev.filter((_, index) => index !== filterIndex); + } + + prev[filterIndex] = filter; + return [...prev]; + } + + // check if the filter already exists + const index = prev.findIndex( + (prevFilter) => + prevFilter.key === filter.key && prevFilter.value === filter.value + ); + + if (index !== -1) { + // remove the filter + if (remove) { + return prev.filter( + (prevFilter) => + prevFilter.key !== filter.key || prevFilter.value !== filter.value + ); + } + + // replace the filter + prev[index] = filter; + return [...prev]; } - return [...prev, value]; + return [...prev, filter]; }); }; const applyFilters = () => { - const validAdvancedFilters = advancedFilters.filter( - (filter) => - filter.value !== null && - filter.value !== undefined && - filter.value !== "" - ); + const filters = [...advancedFilters]; - const convertedFilters = selectedFilters.map((filter) => ({ - key: "name", - operation: "EQUALS", - value: filter, - type: "property", - })); - - const convertedAdvancedFilters = validAdvancedFilters.map((filter) => ({ - key: filter.attribute, - operation: filter.operator.toUpperCase().replace(" ", "_"), - value: filter.value, - type: "attribute", - })); - - if (selectedUserId) { - convertedAdvancedFilters.push({ + if (userId) { + filters.push({ key: "user_id", operation: "EQUALS", - value: selectedUserId, + value: userId, type: "attribute", }); } - if (selectedPromptId) { - convertedAdvancedFilters.push({ + if (promptId) { + filters.push({ key: "prompt_id", operation: "EQUALS", - value: selectedPromptId, + value: promptId, type: "attribute", }); } - onApplyFilters({ - filters: [...convertedFilters, ...convertedAdvancedFilters], - }); + onApplyFilters(filters); onClose(); }; - const addAdvancedFilter = () => { - setAdvancedFilters([ - ...advancedFilters, - { attribute: "", operator: "EQUALS", value: "" }, - ]); - }; - - const removeAdvancedFilter = (index: number) => { - setAdvancedFilters(advancedFilters.filter((_, i) => i !== index)); - }; - - const updateAdvancedFilter = (index: number, key: string, value: any) => { - const updatedFilters = advancedFilters.map((filter, i) => - i === index ? { ...filter, [key]: value } : filter - ); - setAdvancedFilters(updatedFilters); - }; - return ( - -
- - Filter Traces - - Select filters to apply to the traces. - - - -
-

Attributes

- {advancedFilters.map((filter, index) => ( -
+ + + Filter Traces + + Filter traces by various attributes + + + +
+

+ Filter by Attributes +

+
+ Attributes are key-value pairs that are attached to spans. + Attributes capture useful information about the operation like the + model API settings in the case of LLM API calls. You can filter + traces based on these attributes. To learn more about attributes, + check out the{" "} + + + OpenTelemetry specification here + + + . +
+ {advancedFilters.map((filter, index) => { + if (filter.type !== "attribute") return null; + return ( +
- updateAdvancedFilter(index, "attribute", attribute) - } + initialAttribute={filter.key} + setSelectedAttribute={(attribute) => { + handleFilterChange( + { ...filter, key: attribute }, + index, + false + ); + }} /> - - updateAdvancedFilter(index, "operator", operator) - } + { + handleFilterChange( + { ...filter, operation: operator }, + index, + false + ); + }} /> - updateAdvancedFilter(index, "value", e.target.value) - } - className="mr-2" + onChange={(e) => { + handleFilterChange( + { ...filter, value: e.target.value }, + index, + false + ); + }} + className="mr-2 w-48" />
- ))} - + +
+
+
+

+ Filter by User ID +

+ + - Add Attribute - + Learn more +
-
-

User Id

- -
-
-

Prompt Id

- + + +
+
+
+

+ Filter by Prompt ID +

+ + + Learn more +
- - - {(selectedFilters.length > 0 || - advancedFilters.length > 0 || - selectedUserId !== "" || - selectedPromptId !== "") && ( - - )} - - + +
+ + + {(advancedFilters.length > 0 || userId !== "" || promptId !== "") && ( + + )} + +
); @@ -239,24 +304,7 @@ export function AttributesCombobox({ initialAttribute: string; }) { const [open, setOpen] = useState(false); - const [selectedAttribute, setSelectedAttributeState] = useState( - initialAttribute || "" - ); - const [searchQuery, setSearchQuery] = useState(""); - - const filteredAttributes = searchQuery - ? SpanAttributes.filter((attribute) => - attribute.toLowerCase().includes(searchQuery.toLowerCase()) - ) - : SpanAttributes; - - const onInputChange = (value: string) => { - setSearchQuery(value); - }; - - useEffect(() => { - setSelectedAttributeState(initialAttribute); - }, [initialAttribute]); + const [value, setValue] = useState(initialAttribute || ""); return ( @@ -267,39 +315,31 @@ export function AttributesCombobox({ aria-expanded={open} className="w-[250px] justify-between" > - {selectedAttribute ? selectedAttribute : "Select attribute..."} + {value + ? SpanAttributes.find((attr) => attr === value) + : "Select attribute..."} - + - + No attribute found. - {filteredAttributes.map((attribute) => ( + {SpanAttributes.map((attribute) => ( { - setSelectedAttributeState( - currentValue === selectedAttribute ? "" : currentValue - ); - setSelectedAttribute( - currentValue === selectedAttribute ? "" : currentValue - ); + setValue(currentValue); + setSelectedAttribute(currentValue); setOpen(false); }} > {attribute} @@ -313,19 +353,21 @@ export function AttributesCombobox({ ); } -export function OperatorCombox({ +export function OperatorCombobox({ setSelectedOperator, initialOperator, }: { - setSelectedOperator: (attribute: string) => void; - initialOperator: string; + setSelectedOperator: ( + attribute: "EQUALS" | "CONTAINS" | "NOT_EQUALS" + ) => void; + initialOperator: "EQUALS" | "CONTAINS" | "NOT_EQUALS"; }) { const [open, setOpen] = useState(false); - const [selectedOperator, setSelectedOperatorState] = useState( - initialOperator.toLowerCase() || "" + const [value, setValue] = useState( + initialOperator.toUpperCase() || "EQUALS" ); - const comparisonOperators = ["equals", "contains", "not equals"]; + const comparisonOperators = ["EQUALS", "CONTAINS", "NOT_EQUALS"]; return ( @@ -334,13 +376,15 @@ export function OperatorCombox({ variant="outline" role="combobox" aria-expanded={open} - className="w-[200px] justify-between" + className="w-[150px] justify-between" > - {selectedOperator ? selectedOperator : "Select operator..."} + {value + ? comparisonOperators.find((op) => op === value) + : "Select operator..."} - + {comparisonOperators.map((operator) => ( @@ -348,21 +392,22 @@ export function OperatorCombox({ key={operator} value={operator} onSelect={(currentValue) => { - setSelectedOperatorState( - currentValue === selectedOperator ? "" : currentValue - ); + setValue(currentValue.toUpperCase()); setSelectedOperator( - currentValue === selectedOperator ? "" : currentValue + currentValue.toUpperCase() as + | "EQUALS" + | "CONTAINS" + | "NOT_EQUALS" ); setOpen(false); }} > - {operator.toLowerCase()} + {operator} ))} diff --git a/components/project/traces/traces.tsx b/components/project/traces/traces.tsx index 284a72aa..b6f28977 100644 --- a/components/project/traces/traces.tsx +++ b/components/project/traces/traces.tsx @@ -58,7 +58,7 @@ export default function Traces({ email }: { email: string }) { projectId: project_id, filters: { filters: filters, - operation: "AND", + operation: "OR", }, }; @@ -133,12 +133,8 @@ export default function Traces({ email }: { email: string }) { }, ]; - const handleFilterDialogClose = () => { - setIsFilterDialogOpen(false); - }; - - const handleApplyFilters = (newFilters: any) => { - setFilters(newFilters.filters); + const handleApplyFilters = (newFilters: PropertyFilter[]) => { + setFilters(newFilters); setIsFilterDialogOpen(false); }; @@ -248,7 +244,7 @@ export default function Traces({ email }: { email: string }) { )} setIsFilterDialogOpen(false)} onApplyFilters={handleApplyFilters} />
diff --git a/components/project/traces/vendor-dropdown.tsx b/components/project/traces/vendor-dropdown.tsx index b8cb80b0..878e75a0 100644 --- a/components/project/traces/vendor-dropdown.tsx +++ b/components/project/traces/vendor-dropdown.tsx @@ -1,3 +1,4 @@ +import { Info } from "@/components/shared/info"; import { Accordion, AccordionContent, @@ -5,55 +6,166 @@ import { AccordionTrigger, } from "@/components/ui/accordion"; import { Checkbox } from "@/components/ui/checkbox"; +import { Separator } from "@/components/ui/separator"; +import { OTEL_GENAI_EVENTS, SUPPORTED_VENDORS } from "@/lib/constants"; +import { PropertyFilter } from "@/lib/services/query_builder_service"; import { EventsLocal, TracedFunctionsByVendorsLocal, VendorsLocal, } from "@/lib/ts_sdk_constants"; +import { cn } from "@/lib/utils"; +import { ChevronDown } from "lucide-react"; +import { useState } from "react"; export default function VendorDropdown({ selectedFilters, handleFilterChange, }: { - selectedFilters: string[]; - handleFilterChange: (method: string) => void; + selectedFilters: PropertyFilter[]; + handleFilterChange: (filter: PropertyFilter) => void; }) { + const [showFunctionFilters, setShowFunctionFilters] = useState(false); return ( + + Filter by Vendor + +
+

+ Vendors are the SDKs that are used to instrument the code. Vendors + include LLMs, VectorDBs and frameworks such as Langchain, CrewAI + etc. +

+ {Object.keys(SUPPORTED_VENDORS).map((vendor) => ( +
+ + filter.value === SUPPORTED_VENDORS[vendor] && + filter.key === "langtrace.service.name" + )} + value={SUPPORTED_VENDORS[vendor]} + onClick={() => + handleFilterChange({ + key: "langtrace.service.name", + operation: "EQUALS", + value: SUPPORTED_VENDORS[vendor], + type: "attribute", + }) + } + /> + +
+ ))} +
+
+
+ + {showFunctionFilters && + Object.values(VendorsLocal).map((vendor) => ( + + {vendor} + +
+ {TracedFunctionsByVendorsLocal[vendor as VendorsLocal].map( + (method: any) => ( +
+ + filter.value === method && filter.key === "name" + )} + value={method} + onClick={() => { + handleFilterChange({ + key: "name", + operation: "CONTAINS", + value: method, + type: "property", + }); + }} + /> + +
+ ) + )} +
+
+
+ ))} + - events + Filter by Events - {EventsLocal.map((event) => ( -
- handleFilterChange(event)} - /> - +
+
+ Events are part of the OpenTelemetry specification. In the context + of Langtrace, events are the logs that are generated by the code. + Events are used to track the prompt and response content in the + case of LLMs. To learn more about events, refer to the official{" "} + + + OpenTelemetry specification here + + + .
- ))} + {EventsLocal.map((event) => ( +
+ filter.value === event && filter.key === "name" + )} + value={event} + onClick={() => { + handleFilterChange({ + key: "name", + operation: "EQUALS", + value: event, + type: "event", + }); + }} + /> + +
+ ))} +
- {Object.values(VendorsLocal).map((vendor) => ( - - {vendor} - - {TracedFunctionsByVendorsLocal[vendor as VendorsLocal].map( - (method: any) => ( -
- handleFilterChange(method)} - value={method} - /> - -
- ) - )} -
-
- ))} ); } diff --git a/components/shared/create-prompt-dialog.tsx b/components/shared/create-prompt-dialog.tsx index fae23fab..8c0d1b1b 100644 --- a/components/shared/create-prompt-dialog.tsx +++ b/components/shared/create-prompt-dialog.tsx @@ -26,6 +26,7 @@ import { Label } from "@/components/ui/label"; import { zodResolver } from "@hookform/resolvers/zod"; import { Prompt } from "@prisma/client"; import CodeEditor from "@uiw/react-textarea-code-editor"; +import { X } from "lucide-react"; import { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import { useQueryClient } from "react-query"; @@ -99,6 +100,12 @@ export default function CreatePromptDialog({ return vars; }; + const handleRemoveVariable = (variableToRemove: string) => { + setVariables((prevVariables) => + prevVariables.filter((variable) => variable !== variableToRemove) + ); + }; + useEffect(() => { if (passedPrompt) { setPrompt(passedPrompt); @@ -129,7 +136,7 @@ export default function CreatePromptDialog({ )} - + {!confirmAndSaveView ? "Review and Save" : "Create new prompt"} @@ -214,12 +221,17 @@ export default function CreatePromptDialog({
{variables.map((variable) => ( - - {variable} - + {variable} + handleRemoveVariable(variable)} + /> +
))}
diff --git a/components/shared/prompt-combobox.tsx b/components/shared/prompt-combobox.tsx index 68ebe628..95d52499 100644 --- a/components/shared/prompt-combobox.tsx +++ b/components/shared/prompt-combobox.tsx @@ -1,5 +1,3 @@ -"use client"; - import { Button } from "@/components/ui/button"; import { Command, @@ -16,10 +14,11 @@ import { import { Check, ChevronsUpDown } from "lucide-react"; import { useParams } from "next/navigation"; -import { useEffect, useState } from "react"; +import { useState } from "react"; import { useQuery } from "react-query"; import { toast } from "sonner"; +import { Skeleton } from "../ui/skeleton"; export function PromptCombobox({ setSelectedPrompt, @@ -33,11 +32,7 @@ export function PromptCombobox({ const [selectedPromptId, setSelectedPromptIdState] = useState( selectedPrompt || "" ); - const [searchQuery, setSearchQuery] = useState(""); const [promptIds, setPromptIds] = useState([]); - const [showLoader, setShowLoader] = useState(false); - const [internalSelectedPrompt, setInternalSelectedPrompt] = - useState(selectedPrompt); const handleSelectPrompt = (currentValue: string) => { const newPromptId = currentValue === selectedPromptId ? "" : currentValue; @@ -47,11 +42,6 @@ export function PromptCombobox({ setOpen(false); }; - useEffect(() => { - setSelectedPromptIdState(selectedPrompt || ""); - setInternalSelectedPrompt(selectedPrompt || ""); - }, [selectedPrompt]); - const fetchPromptIds = useQuery({ queryKey: ["fetch-prompt-ids-query", project_id], queryFn: async () => { @@ -67,7 +57,6 @@ export function PromptCombobox({ setPromptIds(data?.promptIDs || []); }, onError: (error) => { - setShowLoader(false); toast.error("Failed to fetch prompt ids", { description: error instanceof Error ? error.message : String(error), }); @@ -75,13 +64,9 @@ export function PromptCombobox({ }); if (fetchPromptIds.isLoading) { - return
Loading...
; + return ; } - const onInputChange = (value: string) => { - setSearchQuery(value); - }; - return ( @@ -91,42 +76,44 @@ export function PromptCombobox({ aria-expanded={open} className="w-[220px] justify-between" > - {selectedPromptId ? selectedPromptId : "Filter by prompt id..."} + {selectedPromptId ? selectedPromptId : "select prompt id..."} - - No attribute found. + + No prompt IDs found. - {promptIds.map((id: string) => ( - { - setSelectedPromptIdState( - currentValue === selectedPromptId ? "" : currentValue - ); - setSelectedPrompt( - currentValue === selectedPromptId ? "" : currentValue - ); - handleSelectPrompt(currentValue); - setOpen(false); - }} - > - - {id} + {promptIds.length > 0 ? ( + promptIds.map((id: string) => ( + { + setSelectedPromptIdState( + currentValue === selectedPromptId ? "" : currentValue + ); + setSelectedPrompt( + currentValue === selectedPromptId ? "" : currentValue + ); + handleSelectPrompt(currentValue); + setOpen(false); + }} + > + + {id} + + )) + ) : ( + + No Prompt IDs found. - ))} + )} diff --git a/components/shared/user-combobox.tsx b/components/shared/user-combobox.tsx index bd9e5fd3..bc776f1c 100644 --- a/components/shared/user-combobox.tsx +++ b/components/shared/user-combobox.tsx @@ -60,7 +60,7 @@ export function UserCombobox({ aria-expanded={open} className="w-[200px] justify-between" > - {selectedUser ? selectedUser : "Filter by user id..."} + {selectedUser ? selectedUser : "select user id..."} diff --git a/components/shared/vendor-metadata.tsx b/components/shared/vendor-metadata.tsx index 606eb29c..b45f8374 100644 --- a/components/shared/vendor-metadata.tsx +++ b/components/shared/vendor-metadata.tsx @@ -127,6 +127,14 @@ export function vendorColor(vendor: string) { return "bg-blue-200"; } + if (vendor.includes("vertex")) { + return "bg-blue-200"; + } + + if (vendor.includes("gemini")) { + return "bg-blue-200"; + } + return "bg-gray-800"; } @@ -372,6 +380,38 @@ export function VendorLogo({ ); } + if (vendor.includes("vertex")) { + const color = vendorColor("vertex"); + return ( + Vertex AI Logo + ); + } + + if (vendor.includes("gemini")) { + const color = vendorColor("gemini"); + return ( + Gemini Logo + ); + } + return (
= ({ color = "bg-red-500"; else if (span.name.includes("crewai") || serviceName.includes("crewai")) color = "bg-red-500"; + else if (span.name.includes("weaviate") || serviceName.includes("weaviate")) + color = "bg-green-500"; + else if (span.name.includes("pg") || serviceName.includes("pg")) + color = "bg-blue-500"; + else if (span.name.includes("gemini") || serviceName.includes("gemini")) + color = "bg-blue-500"; + else if (span.name.includes("vertex") || serviceName.includes("vertex")) + color = "bg-blue-500"; else if ( span.name.includes("llamaindex") || serviceName.includes("llamaindex") diff --git a/components/ui/menubar.tsx b/components/ui/menubar.tsx new file mode 100644 index 00000000..010145cd --- /dev/null +++ b/components/ui/menubar.tsx @@ -0,0 +1,240 @@ +"use client" + +import * as React from "react" +import { + CheckIcon, + ChevronRightIcon, + DotFilledIcon, +} from "@radix-ui/react-icons" +import * as MenubarPrimitive from "@radix-ui/react-menubar" + +import { cn } from "@/lib/utils" + +const MenubarMenu = MenubarPrimitive.Menu + +const MenubarGroup = MenubarPrimitive.Group + +const MenubarPortal = MenubarPrimitive.Portal + +const MenubarSub = MenubarPrimitive.Sub + +const MenubarRadioGroup = MenubarPrimitive.RadioGroup + +const Menubar = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +Menubar.displayName = MenubarPrimitive.Root.displayName + +const MenubarTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +MenubarTrigger.displayName = MenubarPrimitive.Trigger.displayName + +const MenubarSubTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean + } +>(({ className, inset, children, ...props }, ref) => ( + + {children} + + +)) +MenubarSubTrigger.displayName = MenubarPrimitive.SubTrigger.displayName + +const MenubarSubContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +MenubarSubContent.displayName = MenubarPrimitive.SubContent.displayName + +const MenubarContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>( + ( + { className, align = "start", alignOffset = -4, sideOffset = 8, ...props }, + ref + ) => ( + + + + ) +) +MenubarContent.displayName = MenubarPrimitive.Content.displayName + +const MenubarItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean + } +>(({ className, inset, ...props }, ref) => ( + +)) +MenubarItem.displayName = MenubarPrimitive.Item.displayName + +const MenubarCheckboxItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, checked, ...props }, ref) => ( + + + + + + + {children} + +)) +MenubarCheckboxItem.displayName = MenubarPrimitive.CheckboxItem.displayName + +const MenubarRadioItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + + + + + {children} + +)) +MenubarRadioItem.displayName = MenubarPrimitive.RadioItem.displayName + +const MenubarLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean + } +>(({ className, inset, ...props }, ref) => ( + +)) +MenubarLabel.displayName = MenubarPrimitive.Label.displayName + +const MenubarSeparator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +MenubarSeparator.displayName = MenubarPrimitive.Separator.displayName + +const MenubarShortcut = ({ + className, + ...props +}: React.HTMLAttributes) => { + return ( + + ) +} +MenubarShortcut.displayname = "MenubarShortcut" + +export { + Menubar, + MenubarMenu, + MenubarTrigger, + MenubarContent, + MenubarItem, + MenubarSeparator, + MenubarLabel, + MenubarCheckboxItem, + MenubarRadioGroup, + MenubarRadioItem, + MenubarPortal, + MenubarSubContent, + MenubarSubTrigger, + MenubarGroup, + MenubarSub, + MenubarShortcut, +} diff --git a/components/ui/tabs.tsx b/components/ui/tabs.tsx new file mode 100644 index 00000000..0f4caebb --- /dev/null +++ b/components/ui/tabs.tsx @@ -0,0 +1,55 @@ +"use client" + +import * as React from "react" +import * as TabsPrimitive from "@radix-ui/react-tabs" + +import { cn } from "@/lib/utils" + +const Tabs = TabsPrimitive.Root + +const TabsList = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +TabsList.displayName = TabsPrimitive.List.displayName + +const TabsTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +TabsTrigger.displayName = TabsPrimitive.Trigger.displayName + +const TabsContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +TabsContent.displayName = TabsPrimitive.Content.displayName + +export { Tabs, TabsList, TabsTrigger, TabsContent } diff --git a/lib/constants.ts b/lib/constants.ts index b7d82503..3343dfb7 100644 --- a/lib/constants.ts +++ b/lib/constants.ts @@ -7,9 +7,17 @@ import { Test } from "@prisma/client"; import { TiktokenEncoding } from "js-tiktoken"; export const EVALUATIONS_DOCS_URL = "https://docs.langtrace.ai/features/evaluations"; - +export const HOW_TO_USER_ID = + "https://docs.langtrace.ai/features/attach_user_id"; +export const HOW_TO_PROMPT_FETCHING = + "https://docs.langtrace.ai/features/attach_prompt_id"; export const SCHEDULE_CALL_LINK = "https://calendar.app.google/Go5gXNPcqZjAY4i47"; +export const OTEL_GENAI = "https://opentelemetry.io/docs/specs/semconv/gen-ai/"; +export const OTEL_GENAI_EVENTS = + "https://opentelemetry.io/docs/specs/semconv/gen-ai/llm-spans/#events"; +export const OTEL_GENAI_ATTRIBUTES = + "https://opentelemetry.io/docs/specs/semconv/gen-ai/llm-spans/#llm-request-attributes"; // TODO: Add more models // https://github.com/dqbd/tiktoken/blob/74c147e19584a3a1acea0c8e0da4d39415cd33e0/wasm/src/lib.rs#L328 @@ -240,3 +248,26 @@ export const LLM_VENDORS = [ label: "Perplexity", }, ]; + +export const SUPPORTED_VENDORS: Record = { + ANTHROPIC: "Anthropic", + AZURE: "Azure", + CHROMA: "Chroma", + CREWAI: "CrewAI", + DSPY: "DSPy", + GROQ: "Groq", + LANGCHAIN: "Langchain", + LANGCHAIN_COMMUNITY: "Langchain Community", + LANGCHAIN_CORE: "Langchain Core", + LANGGRAPH: "Langgraph", + LLAMAINDEX: "LlamaIndex", + OPENAI: "OpenAI", + PINECONE: "Pinecone", + COHERE: "Cohere", + PPLX: "Perplexity", + QDRANT: "Qdrant", + WEAVIATE: "Weaviate", + OLLAMA: "Ollama", + VERTEXAI: "VertexAI", + GEMINI: "Gemini", +}; diff --git a/lib/services/query_builder_service.ts b/lib/services/query_builder_service.ts index fc0bb21d..d6c16d36 100644 --- a/lib/services/query_builder_service.ts +++ b/lib/services/query_builder_service.ts @@ -76,10 +76,10 @@ export class QueryBuilderService implements IQueryBuilderService { condition = `${eventName} = '${filter.value}', JSONExtractArrayRaw(events))`; break; case "CONTAINS": - condition = `${eventName} LIKE '%${filter.value}%'`; + condition = `${eventName} LIKE '%${filter.value}%', JSONExtractArrayRaw(events))`; break; case "NOT_EQUALS": - condition = `${eventName} != '${filter.value}'`; + condition = `${eventName} != '${filter.value}', JSONExtractArrayRaw(events))`; break; default: throw new Error(`Unsupported filter operation: ${filter.operation}`); @@ -267,7 +267,7 @@ export class QueryBuilderService implements IQueryBuilderService { }); if (whereConditions.length > 0) { - baseQuery += ` WHERE (${whereConditions.join(` ${filters.operation} `)})`; + baseQuery += ` AND (${whereConditions.join(` ${filters.operation} `)})`; } return baseQuery; @@ -299,7 +299,7 @@ export class QueryBuilderService implements IQueryBuilderService { }); if (whereConditions.length > 0) { - baseQuery += ` WHERE (${whereConditions.join(` ${filters.operation} `)})`; + baseQuery += ` AND (${whereConditions.join(` ${filters.operation} `)})`; } return baseQuery; diff --git a/lib/services/trace_service.ts b/lib/services/trace_service.ts index 9de5dad5..94b55cae 100644 --- a/lib/services/trace_service.ts +++ b/lib/services/trace_service.ts @@ -546,6 +546,7 @@ export class TraceService implements ITraceService { filters: Filter = { operation: "AND", filters: [] } ): Promise> { try { + // check if the table exists const tableExists = await this.client.checkTableExists(project_id); if (!tableExists) { return { diff --git a/lib/utils.ts b/lib/utils.ts index 7a3abb67..d3d41063 100644 --- a/lib/utils.ts +++ b/lib/utils.ts @@ -602,6 +602,10 @@ export function getVendorFromSpan(span: Span): string { vendor = "dspy"; } else if (span.name.includes("crewai") || serviceName.includes("crewai")) { vendor = "crewai"; + } else if (span.name.includes("vertex") || serviceName.includes("vertex")) { + vendor = "vertex"; + } else if (span.name.includes("gemini") || serviceName.includes("gemini")) { + vendor = "gemini"; } return vendor; } diff --git a/package-lock.json b/package-lock.json index 5f586d1c..d9bec22e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,7 @@ "@headlessui/react": "^1.7.18", "@headlessui/tailwindcss": "^0.2.0", "@hookform/resolvers": "^3.3.4", - "@langtrase/trace-attributes": "^6.0.1", + "@langtrase/trace-attributes": "^6.0.5", "@langtrase/typescript-sdk": "^3.3.2", "@mui/icons-material": "^5.15.14", "@mui/material": "^5.15.14", @@ -34,12 +34,14 @@ "@radix-ui/react-hover-card": "^1.0.7", "@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-label": "^2.0.2", + "@radix-ui/react-menubar": "^1.1.1", "@radix-ui/react-popover": "^1.0.7", "@radix-ui/react-scroll-area": "^1.1.0", "@radix-ui/react-select": "^2.0.0", "@radix-ui/react-separator": "^1.0.3", "@radix-ui/react-slot": "^1.0.2", "@radix-ui/react-switch": "^1.0.3", + "@radix-ui/react-tabs": "^1.1.0", "@remixicon/react": "^4.2.0", "@tanstack/react-query": "^5.17.10", "@tremor/react": "^3.14.1", @@ -67,7 +69,7 @@ "lucide-react": "^0.323.0", "mammoth": "^1.7.0", "markdown-it": "^14.0.0", - "next": "14.1.1", + "next": "14.1.0", "next-auth": "^4.24.5", "next-themes": "^0.2.1", "npm": "^10.7.0", @@ -1243,8 +1245,9 @@ } }, "node_modules/@langtrase/trace-attributes": { - "version": "6.0.1", - "license": "MIT", + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/@langtrase/trace-attributes/-/trace-attributes-6.0.5.tgz", + "integrity": "sha512-XVjBpKX02Z+rBwYz38g15dgV8102GB9QoFCuxUDeFroq4SH5NUHKrm2kbMPq9Tr3QVfvkWWNekdPMKLk9wVsSQ==", "dependencies": { "json-schema-to-typescript": "^14.1.0", "ncp": "^2.0.0", @@ -1556,9 +1559,9 @@ } }, "node_modules/@next/env": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/@next/env/-/env-14.1.1.tgz", - "integrity": "sha512-7CnQyD5G8shHxQIIg3c7/pSeYFeMhsNbpU/bmvH7ZnDql7mNRgg8O2JZrhrc/soFnfBnKP4/xXNiiSIPn2w8gA==" + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/env/-/env-14.1.0.tgz", + "integrity": "sha512-Py8zIo+02ht82brwwhTg36iogzFqGLPXlRGKQw5s+qP/kMNc4MAyDeEwBKDijk6zTIbegEgu8Qy7C1LboslQAw==" }, "node_modules/@next/eslint-plugin-next": { "version": "14.1.0", @@ -1569,9 +1572,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.1.1.tgz", - "integrity": "sha512-yDjSFKQKTIjyT7cFv+DqQfW5jsD+tVxXTckSe1KIouKk75t1qZmj/mV3wzdmFb0XHVGtyRjDMulfVG8uCKemOQ==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.1.0.tgz", + "integrity": "sha512-nUDn7TOGcIeyQni6lZHfzNoo9S0euXnu0jhsbMOmMJUBfgsnESdjN97kM7cBqQxZa8L/bM9om/S5/1dzCrW6wQ==", "cpu": [ "arm64" ], @@ -1584,9 +1587,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.1.1.tgz", - "integrity": "sha512-KCQmBL0CmFmN8D64FHIZVD9I4ugQsDBBEJKiblXGgwn7wBCSe8N4Dx47sdzl4JAg39IkSN5NNrr8AniXLMb3aw==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.1.0.tgz", + "integrity": "sha512-1jgudN5haWxiAl3O1ljUS2GfupPmcftu2RYJqZiMJmmbBT5M1XDffjUtRUzP4W3cBHsrvkfOFdQ71hAreNQP6g==", "cpu": [ "x64" ], @@ -1599,9 +1602,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.1.1.tgz", - "integrity": "sha512-YDQfbWyW0JMKhJf/T4eyFr4b3tceTorQ5w2n7I0mNVTFOvu6CGEzfwT3RSAQGTi/FFMTFcuspPec/7dFHuP7Eg==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.1.0.tgz", + "integrity": "sha512-RHo7Tcj+jllXUbK7xk2NyIDod3YcCPDZxj1WLIYxd709BQ7WuRYl3OWUNG+WUfqeQBds6kvZYlc42NJJTNi4tQ==", "cpu": [ "arm64" ], @@ -1614,9 +1617,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.1.1.tgz", - "integrity": "sha512-fiuN/OG6sNGRN/bRFxRvV5LyzLB8gaL8cbDH5o3mEiVwfcMzyE5T//ilMmaTrnA8HLMS6hoz4cHOu6Qcp9vxgQ==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.1.0.tgz", + "integrity": "sha512-v6kP8sHYxjO8RwHmWMJSq7VZP2nYCkRVQ0qolh2l6xroe9QjbgV8siTbduED4u0hlk0+tjS6/Tuy4n5XCp+l6g==", "cpu": [ "arm64" ], @@ -1629,9 +1632,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.1.1.tgz", - "integrity": "sha512-rv6AAdEXoezjbdfp3ouMuVqeLjE1Bin0AuE6qxE6V9g3Giz5/R3xpocHoAi7CufRR+lnkuUjRBn05SYJ83oKNQ==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.1.0.tgz", + "integrity": "sha512-zJ2pnoFYB1F4vmEVlb/eSe+VH679zT1VdXlZKX+pE66grOgjmKJHKacf82g/sWE4MQ4Rk2FMBCRnX+l6/TVYzQ==", "cpu": [ "x64" ], @@ -1644,9 +1647,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.1.1.tgz", - "integrity": "sha512-YAZLGsaNeChSrpz/G7MxO3TIBLaMN8QWMr3X8bt6rCvKovwU7GqQlDu99WdvF33kI8ZahvcdbFsy4jAFzFX7og==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.1.0.tgz", + "integrity": "sha512-rbaIYFt2X9YZBSbH/CwGAjbBG2/MrACCVu2X0+kSykHzHnYH5FjHxwXLkcoJ10cX0aWCEynpu+rP76x0914atg==", "cpu": [ "x64" ], @@ -1659,9 +1662,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.1.1.tgz", - "integrity": "sha512-1L4mUYPBMvVDMZg1inUYyPvFSduot0g73hgfD9CODgbr4xiTYe0VOMTZzaRqYJYBA9mana0x4eaAaypmWo1r5A==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.1.0.tgz", + "integrity": "sha512-o1N5TsYc8f/HpGt39OUQpQ9AKIGApd3QLueu7hXk//2xq5Z9OxmV6sQfNp8C7qYmiOlHYODOGqNNa0e9jvchGQ==", "cpu": [ "arm64" ], @@ -1674,9 +1677,9 @@ } }, "node_modules/@next/swc-win32-ia32-msvc": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.1.1.tgz", - "integrity": "sha512-jvIE9tsuj9vpbbXlR5YxrghRfMuG0Qm/nZ/1KDHc+y6FpnZ/apsgh+G6t15vefU0zp3WSpTMIdXRUsNl/7RSuw==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.1.0.tgz", + "integrity": "sha512-XXIuB1DBRCFwNO6EEzCTMHT5pauwaSj4SWs7CYnME57eaReAKBXCnkUE80p/pAZcewm7hs+vGvNqDPacEXHVkw==", "cpu": [ "ia32" ], @@ -1689,9 +1692,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.1.1.tgz", - "integrity": "sha512-S6K6EHDU5+1KrBDLko7/c1MNy/Ya73pIAmvKeFwsF4RmBFJSO7/7YeD4FnZ4iBdzE69PpQ4sOMU9ORKeNuxe8A==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.1.0.tgz", + "integrity": "sha512-9WEbVRRAqJ3YFVqEZIxUqkiO8l1nool1LmNxygr5HWF8AcSYsEpneUDhmjUVJEzO2A04+oPtZdombzzPPkTtgg==", "cpu": [ "x64" ], @@ -2365,55 +2368,571 @@ }, "peerDependencies": { "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menu": { + "version": "2.0.6", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-collection": "1.0.3", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-direction": "1.0.1", + "@radix-ui/react-dismissable-layer": "1.0.5", + "@radix-ui/react-focus-guards": "1.0.1", + "@radix-ui/react-focus-scope": "1.0.4", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-popper": "1.1.3", + "@radix-ui/react-portal": "1.0.4", + "@radix-ui/react-presence": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-roving-focus": "1.0.4", + "@radix-ui/react-slot": "1.0.2", + "@radix-ui/react-use-callback-ref": "1.0.1", + "aria-hidden": "^1.1.1", + "react-remove-scroll": "2.5.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menubar": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menubar/-/react-menubar-1.1.1.tgz", + "integrity": "sha512-V05Hryq/BE2m+rs8d5eLfrS0jmSWSDHEbG7jEyLA5D5J9jTvWj/o3v3xDN9YsOlH6QIkJgiaNDaP+S4T1rdykw==", + "dependencies": { + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-collection": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.0", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-menu": "2.1.1", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-roving-focus": "1.1.0", + "@radix-ui/react-use-controllable-state": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menubar/node_modules/@radix-ui/primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.0.tgz", + "integrity": "sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA==" + }, + "node_modules/@radix-ui/react-menubar/node_modules/@radix-ui/react-arrow": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.0.tgz", + "integrity": "sha512-FmlW1rCg7hBpEBwFbjHwCW6AmWLQM6g/v0Sn8XbP9NvmSZ2San1FpQeyPtufzOMSIx7Y4dzjlHoifhp+7NkZhw==", + "dependencies": { + "@radix-ui/react-primitive": "2.0.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menubar/node_modules/@radix-ui/react-collection": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.0.tgz", + "integrity": "sha512-GZsZslMJEyo1VKm5L1ZJY8tGDxZNPAoUeQUIbKeJfoi7Q4kmig5AsgLMYYuyYbfjd8fBmFORAIwYAkXMnXZgZw==", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-slot": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menubar/node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.0.tgz", + "integrity": "sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menubar/node_modules/@radix-ui/react-context": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.0.tgz", + "integrity": "sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menubar/node_modules/@radix-ui/react-direction": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.0.tgz", + "integrity": "sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menubar/node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.0.tgz", + "integrity": "sha512-/UovfmmXGptwGcBQawLzvn2jOfM0t4z3/uKffoBlj724+n3FvBbZ7M0aaBOmkp6pqFYpO4yx8tSVJjx3Fl2jig==", + "dependencies": { + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-escape-keydown": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menubar/node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.0.tgz", + "integrity": "sha512-w6XZNUPVv6xCpZUqb/yN9DL6auvpGX3C/ee6Hdi16v2UUy25HV2Q5bcflsiDyT/g5RwbPQ/GIT1vLkeRb+ITBw==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menubar/node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.0.tgz", + "integrity": "sha512-200UD8zylvEyL8Bx+z76RJnASR2gRMuxlgFCPAe/Q/679a/r0eK3MBVYMb7vZODZcffZBdob1EGnky78xmVvcA==", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-callback-ref": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menubar/node_modules/@radix-ui/react-id": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.0.tgz", + "integrity": "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menubar/node_modules/@radix-ui/react-menu": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.1.tgz", + "integrity": "sha512-oa3mXRRVjHi6DZu/ghuzdylyjaMXLymx83irM7hTxutQbD+7IhPKdMdRHD26Rm+kHRrWcrUkkRPv5pd47a2xFQ==", + "dependencies": { + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-collection": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.0", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-dismissable-layer": "1.1.0", + "@radix-ui/react-focus-guards": "1.1.0", + "@radix-ui/react-focus-scope": "1.1.0", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-popper": "1.2.0", + "@radix-ui/react-portal": "1.1.1", + "@radix-ui/react-presence": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-roving-focus": "1.1.0", + "@radix-ui/react-slot": "1.1.0", + "@radix-ui/react-use-callback-ref": "1.1.0", + "aria-hidden": "^1.1.1", + "react-remove-scroll": "2.5.7" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menubar/node_modules/@radix-ui/react-popper": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.0.tgz", + "integrity": "sha512-ZnRMshKF43aBxVWPWvbj21+7TQCvhuULWJ4gNIKYpRlQt5xGRhLx66tMp8pya2UkGHTSlhpXwmjqltDYHhw7Vg==", + "dependencies": { + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0", + "@radix-ui/react-use-rect": "1.1.0", + "@radix-ui/react-use-size": "1.1.0", + "@radix-ui/rect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menubar/node_modules/@radix-ui/react-portal": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.1.tgz", + "integrity": "sha512-A3UtLk85UtqhzFqtoC8Q0KvR2GbXF3mtPgACSazajqq6A41mEQgo53iPzY4i6BwDxlIFqWIhiQ2G729n+2aw/g==", + "dependencies": { + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menubar/node_modules/@radix-ui/react-presence": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.0.tgz", + "integrity": "sha512-Gq6wuRN/asf9H/E/VzdKoUtT8GC9PQc9z40/vEr0VCJ4u5XvvhWIrSsCB6vD2/cH7ugTdSfYq9fLJCcM00acrQ==", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menubar/node_modules/@radix-ui/react-primitive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.0.tgz", + "integrity": "sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==", + "dependencies": { + "@radix-ui/react-slot": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menubar/node_modules/@radix-ui/react-roving-focus": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.0.tgz", + "integrity": "sha512-EA6AMGeq9AEeQDeSH0aZgG198qkfHSbvWTf1HvoDmOB5bBG/qTxjYMWUKMnYiV6J/iP/J8MEFSuB2zRU2n7ODA==", + "dependencies": { + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-collection": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.0", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-controllable-state": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menubar/node_modules/@radix-ui/react-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz", + "integrity": "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menubar/node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz", + "integrity": "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menubar/node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.0.tgz", + "integrity": "sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==", + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menubar/node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.0.tgz", + "integrity": "sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==", + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menubar/node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.0.tgz", + "integrity": "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menubar/node_modules/@radix-ui/react-use-rect": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.0.tgz", + "integrity": "sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==", + "dependencies": { + "@radix-ui/rect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menubar/node_modules/@radix-ui/react-use-size": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.0.tgz", + "integrity": "sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { "optional": true - }, - "@types/react-dom": { - "optional": true } } }, - "node_modules/@radix-ui/react-menu": { - "version": "2.0.6", - "license": "MIT", + "node_modules/@radix-ui/react-menubar/node_modules/@radix-ui/rect": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.0.tgz", + "integrity": "sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==" + }, + "node_modules/@radix-ui/react-menubar/node_modules/react-remove-scroll": { + "version": "2.5.7", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.7.tgz", + "integrity": "sha512-FnrTWO4L7/Bhhf3CYBNArEG/yROV0tKmTv7/3h9QCFvH6sndeFf1wPqOcbFVu5VAulS5dV1wGT3GZZ/1GawqiA==", "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-collection": "1.0.3", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-direction": "1.0.1", - "@radix-ui/react-dismissable-layer": "1.0.5", - "@radix-ui/react-focus-guards": "1.0.1", - "@radix-ui/react-focus-scope": "1.0.4", - "@radix-ui/react-id": "1.0.1", - "@radix-ui/react-popper": "1.1.3", - "@radix-ui/react-portal": "1.0.4", - "@radix-ui/react-presence": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-roving-focus": "1.0.4", - "@radix-ui/react-slot": "1.0.2", - "@radix-ui/react-use-callback-ref": "1.0.1", - "aria-hidden": "^1.1.1", - "react-remove-scroll": "2.5.5" + "react-remove-scroll-bar": "^2.3.4", + "react-style-singleton": "^2.2.1", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.0", + "use-sidecar": "^1.1.2" + }, + "engines": { + "node": ">=10" }, "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" }, "peerDependenciesMeta": { "@types/react": { "optional": true - }, - "@types/react-dom": { - "optional": true } } }, @@ -2883,6 +3402,261 @@ } } }, + "node_modules/@radix-ui/react-tabs": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.0.tgz", + "integrity": "sha512-bZgOKB/LtZIij75FSuPzyEti/XBhJH52ExgtdVqjCIh+Nx/FW+LhnbXtbCzIi34ccyMsyOja8T0thCzoHFXNKA==", + "dependencies": { + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-context": "1.1.0", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-presence": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-roving-focus": "1.1.0", + "@radix-ui/react-use-controllable-state": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.0.tgz", + "integrity": "sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA==" + }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-collection": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.0.tgz", + "integrity": "sha512-GZsZslMJEyo1VKm5L1ZJY8tGDxZNPAoUeQUIbKeJfoi7Q4kmig5AsgLMYYuyYbfjd8fBmFORAIwYAkXMnXZgZw==", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-slot": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.0.tgz", + "integrity": "sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-context": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.0.tgz", + "integrity": "sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-direction": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.0.tgz", + "integrity": "sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-id": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.0.tgz", + "integrity": "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-presence": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.0.tgz", + "integrity": "sha512-Gq6wuRN/asf9H/E/VzdKoUtT8GC9PQc9z40/vEr0VCJ4u5XvvhWIrSsCB6vD2/cH7ugTdSfYq9fLJCcM00acrQ==", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-primitive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.0.tgz", + "integrity": "sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==", + "dependencies": { + "@radix-ui/react-slot": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-roving-focus": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.0.tgz", + "integrity": "sha512-EA6AMGeq9AEeQDeSH0aZgG198qkfHSbvWTf1HvoDmOB5bBG/qTxjYMWUKMnYiV6J/iP/J8MEFSuB2zRU2n7ODA==", + "dependencies": { + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-collection": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.0", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-controllable-state": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz", + "integrity": "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz", + "integrity": "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.0.tgz", + "integrity": "sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==", + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.0.tgz", + "integrity": "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-use-callback-ref": { "version": "1.0.1", "license": "MIT", @@ -9944,11 +10718,11 @@ "license": "MIT" }, "node_modules/next": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/next/-/next-14.1.1.tgz", - "integrity": "sha512-McrGJqlGSHeaz2yTRPkEucxQKe5Zq7uPwyeHNmJaZNY4wx9E9QdxmTp310agFRoMuIYgQrCrT3petg13fSVOww==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/next/-/next-14.1.0.tgz", + "integrity": "sha512-wlzrsbfeSU48YQBjZhDzOwhWhGsy+uQycR8bHAOt1LY1bn3zZEcDyHQOEoN3aWzQ8LHCAJ1nqrWCc9XF2+O45Q==", "dependencies": { - "@next/env": "14.1.1", + "@next/env": "14.1.0", "@swc/helpers": "0.5.2", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001579", @@ -9963,15 +10737,15 @@ "node": ">=18.17.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "14.1.1", - "@next/swc-darwin-x64": "14.1.1", - "@next/swc-linux-arm64-gnu": "14.1.1", - "@next/swc-linux-arm64-musl": "14.1.1", - "@next/swc-linux-x64-gnu": "14.1.1", - "@next/swc-linux-x64-musl": "14.1.1", - "@next/swc-win32-arm64-msvc": "14.1.1", - "@next/swc-win32-ia32-msvc": "14.1.1", - "@next/swc-win32-x64-msvc": "14.1.1" + "@next/swc-darwin-arm64": "14.1.0", + "@next/swc-darwin-x64": "14.1.0", + "@next/swc-linux-arm64-gnu": "14.1.0", + "@next/swc-linux-arm64-musl": "14.1.0", + "@next/swc-linux-x64-gnu": "14.1.0", + "@next/swc-linux-x64-musl": "14.1.0", + "@next/swc-win32-arm64-msvc": "14.1.0", + "@next/swc-win32-ia32-msvc": "14.1.0", + "@next/swc-win32-x64-msvc": "14.1.0" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", diff --git a/package.json b/package.json index b33b2c6a..ce29bfa0 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "@headlessui/react": "^1.7.18", "@headlessui/tailwindcss": "^0.2.0", "@hookform/resolvers": "^3.3.4", - "@langtrase/trace-attributes": "^6.0.1", + "@langtrase/trace-attributes": "^6.0.5", "@langtrase/typescript-sdk": "^3.3.2", "@mui/icons-material": "^5.15.14", "@mui/material": "^5.15.14", @@ -42,12 +42,14 @@ "@radix-ui/react-hover-card": "^1.0.7", "@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-label": "^2.0.2", + "@radix-ui/react-menubar": "^1.1.1", "@radix-ui/react-popover": "^1.0.7", "@radix-ui/react-scroll-area": "^1.1.0", "@radix-ui/react-select": "^2.0.0", "@radix-ui/react-separator": "^1.0.3", "@radix-ui/react-slot": "^1.0.2", "@radix-ui/react-switch": "^1.0.3", + "@radix-ui/react-tabs": "^1.1.0", "@remixicon/react": "^4.2.0", "@tanstack/react-query": "^5.17.10", "@tremor/react": "^3.14.1", diff --git a/public/gemini.jpeg b/public/gemini.jpeg new file mode 100644 index 00000000..41414775 Binary files /dev/null and b/public/gemini.jpeg differ diff --git a/public/vertexai.png b/public/vertexai.png new file mode 100644 index 00000000..3142fdd0 Binary files /dev/null and b/public/vertexai.png differ