Compare commits

...
8 Commits
Author SHA1 Message Date
Marc Klingen b5e1b9916f chore: release v2.4.3
CI/CD / lint (push) Waiting to run
CI/CD / test-docker-build (push) Waiting to run
CI/CD / tests (18) (push) Waiting to run
CI/CD / tests (20) (push) Waiting to run
CI/CD / e2e-tests (push) Waiting to run
CI/CD / all-ci-passed (push) Blocked by required conditions
CI/CD / push-docker-image (push) Blocked by required conditions
release.yml / release (push) Waiting to run
2024-02-15 01:15:58 +01:00
Marc KlingenandGitHub 8075996a1c fix(ui): overflow and responsive ui in trace view (#1164) 2024-02-15 00:13:48 +00:00
Marc KlingenandGitHub 80cc85d756 perf(prompts): add db indicies on project_id and id (#1163) 2024-02-14 23:30:41 +00:00
Marc KlingenandGitHub 45c611bc6d fix(prompts): allow underscores in variables when editing a prompt via UI (#1161) 2024-02-14 23:25:09 +01:00
Richard KrümmelandGitHub 1478aa9c2b fix: dashboard drop down selector on mobile view (#1153) 2024-02-14 14:53:13 +01:00
Marc KlingenandGitHub 4944408b62 fix(cloud): show plan limit only on hobby plan (#1152) 2024-02-14 01:09:18 +00:00
Marc Klingen f6e81f818b fix(cloud): hobby plan usage limit 2024-02-14 01:06:07 +01:00
Marc KlingenandGitHub b2d5839dcd chore(cloud): update stripe billing link (#1149) 2024-02-13 22:03:54 +00:00
12 changed files with 101 additions and 86 deletions
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "langfuse-core",
"version": "2.4.2",
"version": "2.4.3",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "langfuse-core",
"version": "2.4.2",
"version": "2.4.3",
"hasInstallScript": true,
"dependencies": {
"@anthropic-ai/tokenizer": "^0.0.4",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "langfuse-core",
"version": "2.4.2",
"version": "2.4.3",
"private": true,
"scripts": {
"prebuild": "cp generated/openapi-client/openapi.yml public/openapi-client.yml && cp generated/openapi-server/openapi.yml public/openapi-server.yml",
@@ -0,0 +1,5 @@
-- CreateIndex
CREATE INDEX "prompts_project_id_id_idx" ON "prompts"("project_id", "id");
-- CreateIndex
CREATE INDEX "prompts_project_id_idx" ON "prompts"("project_id");
+2
View File
@@ -425,6 +425,8 @@ model Prompt {
@@unique([projectId, name, version])
@@index([projectId, name, version])
@@index([projectId, id])
@@index([projectId])
@@map("prompts")
}
+19 -20
View File
@@ -20,7 +20,6 @@ import { TagTraceDetailsPopover } from "@/src/features/tag/components/TagTraceDe
import useLocalStorage from "@/src/components/useLocalStorage";
import { Toggle } from "@/src/components/ui/toggle";
import { Award, ChevronsDownUp, ChevronsUpDown } from "lucide-react";
import { ScrollArea } from "@/src/components/ui/scroll-area";
import { usdFormatter } from "@/src/utils/numbers";
import Decimal from "decimal.js";
import { useCallback, useState } from "react";
@@ -91,8 +90,8 @@ export function Trace(props: {
}, [setCollapsedObservations]);
return (
<div className="grid gap-4 md:h-full md:grid-cols-5">
<ScrollArea className="md:col-span-3 md:h-full">
<div className="grid gap-4 md:h-full md:grid-cols-5 lg:grid-cols-6 xl:grid-cols-7">
<div className="overflow-y-auto md:col-span-3 md:h-full lg:col-span-4 xl:col-span-5">
{currentObservationId === undefined ||
currentObservationId === "" ||
currentObservationId === null ? (
@@ -110,7 +109,7 @@ export function Trace(props: {
traceId={props.trace.id}
/>
)}
</ScrollArea>
</div>
<div className="md:col-span-2 md:flex md:h-full md:flex-col md:overflow-hidden">
<div className="mb-2 flex flex-shrink-0 flex-row justify-end gap-2">
<Toggle
@@ -138,21 +137,21 @@ export function Trace(props: {
)}
</Toggle>
</div>
<ScrollArea className="flex flex-grow">
<ObservationTree
observations={props.observations}
collapsedObservations={collapsedObservations}
toggleCollapsedObservation={toggleCollapsedObservation}
collapseAll={collapseAll}
expandAll={expandAll}
trace={props.trace}
scores={props.scores}
currentObservationId={currentObservationId ?? undefined}
setCurrentObservationId={setCurrentObservationId}
showMetrics={metricsOnObservationTree}
showScores={scoresOnObservationTree}
/>
</ScrollArea>
<ObservationTree
observations={props.observations}
collapsedObservations={collapsedObservations}
toggleCollapsedObservation={toggleCollapsedObservation}
collapseAll={collapseAll}
expandAll={expandAll}
trace={props.trace}
scores={props.scores}
currentObservationId={currentObservationId ?? undefined}
setCurrentObservationId={setCurrentObservationId}
showMetrics={metricsOnObservationTree}
showScores={scoresOnObservationTree}
className="flex w-full flex-col overflow-y-auto"
/>
</div>
</div>
);
@@ -192,7 +191,7 @@ export function TracePage({ traceId }: { traceId: string }) {
if (trace.error?.data?.code === "UNAUTHORIZED") return <NoAccessError />;
if (!trace.data) return <div>loading...</div>;
return (
<div className="flex flex-col overflow-hidden xl:container md:h-[calc(100vh-2rem)]">
<div className="flex flex-col overflow-hidden 2xl:container md:h-[calc(100vh-2rem)]">
<Header
title="Trace Detail"
breadcrumb={[
+3 -2
View File
@@ -16,13 +16,13 @@ export function JSONView(props: {
const parsedJson = deepParseJson(props.json);
return (
<div className={cn("max-w-full rounded-md border ", props.className)}>
<div className={cn("rounded-md border", props.className)}>
{props.title ? (
<div className="border-b px-3 py-1 text-xs font-medium">
{props.title}
</div>
) : undefined}
<div className="flex gap-2 whitespace-pre-wrap p-3 text-xs">
<div className="flex gap-2 whitespace-pre-wrap break-words p-3 text-xs">
{props.isLoading ? (
<Skeleton className="h-3 w-3/4" />
) : (
@@ -34,6 +34,7 @@ export function JSONView(props: {
displaySize={"collapsed"}
matchesURL={true}
customizeCopy={(node) => stringifyJsonNode(node)}
className="w-full"
/>
)}
</div>
+1 -1
View File
@@ -1 +1 @@
export const VERSION = "v2.4.2";
export const VERSION = "v2.4.3";
@@ -21,6 +21,7 @@ export const TabComponent = ({ tabs }: TabComponentProps) => {
name="tabs"
className="block w-full rounded-md border-gray-300 py-2 pl-3 pr-10 text-base focus:border-indigo-500 focus:outline-none focus:ring-indigo-500 sm:text-sm"
defaultValue={0}
onChange={(e) => setSelectedIndex(Number(e.target.selectedIndex))}
>
{tabs.map((tab) => (
<option key={tab.tabTitle}>{tab.tabTitle}</option>
+1 -1
View File
@@ -9,7 +9,7 @@ export function PricingPage(props: { className?: string }) {
<div className={props.className}>
<stripe-pricing-table
client-reference-id={session.data?.user?.id}
pricing-table-id="prctbl_1OWhugAWilt2EAVVKHi8TEw7"
pricing-table-id="prctbl_1OjTUyAWilt2EAVVMCMAMDgB"
publishable-key="pk_live_51MPW00AWilt2EAVVFWfPTQhgmLA0EeacLSzAs6e3vECCcMBvwcMse81XgXO6k1bdBHbPBdpOmrXE8P1gBrxE7yhH00RPHQ8SyG"
></stripe-pricing-table>
</div>
@@ -77,13 +77,13 @@ const formSchema = z.object({
const variables = extractVariables(val);
const matches = variables.map((variable) => {
// check regex here
if (variable.match(/^[A-Za-z]+$/)) {
if (variable.match(/^[A-Za-z_]+$/)) {
return true;
}
return false;
});
return !matches.includes(false);
}, "Variables must only contain letters"),
}, "Variables must only contain letters and underscores (_)"),
isActive: z.boolean({
required_error: "Enter whether the prompt should go live",
}),
@@ -18,13 +18,13 @@ import { usePostHog } from "posthog-js/react";
export const ProjectUsageChart: React.FC<{ projectId: string }> = ({
projectId,
}) => {
const usage = api.usageMetering.currentMonth.useQuery({
const usage = api.usageMetering.last30d.useQuery({
projectId,
});
const posthog = usePostHog();
const project = api.projects.byId.useQuery({ projectId });
const planLimit =
project.data?.cloudConfig?.monthlyObservationLimit ?? 100_000;
project.data?.cloudConfig?.monthlyObservationLimit ?? 50_000;
const plan = project.data?.cloudConfig?.plan ?? "Hobby";
const currentMonth = new Date().toLocaleDateString("en-US", {
month: "short",
@@ -38,63 +38,70 @@ export const ProjectUsageChart: React.FC<{ projectId: string }> = ({
Usage
</h2>
<Card className="p-4 lg:w-1/2">
{usage.data !== undefined ? (
{usage.data !== undefined && (
<>
<Text>Observations / month</Text>
<Metric>{usage.data}</Metric>
<Flex className="mt-4">
<Text>
{`${currentMonth}: ${usage.data} (${(
(usage.data / planLimit) *
100
).toLocaleString(undefined, {
maximumFractionDigits: 2,
})}%)`}
</Text>
<Text>Plan limit: {simplifyNumber(planLimit)}</Text>
</Flex>
<MarkerBar
value={Math.min((usage.data / planLimit) * 100, 100)}
className="mt-3"
/>
{plan === "Hobby" && (
<>
<Flex className="mt-4">
<Text>
{`${currentMonth}: ${usage.data} (${(
(usage.data / planLimit) *
100
).toLocaleString(undefined, {
maximumFractionDigits: 2,
})}%)`}
</Text>
<Text>Plan limit: {simplifyNumber(planLimit)}</Text>
</Flex>
<MarkerBar
value={Math.min((usage.data / planLimit) * 100, 100)}
className="mt-3"
/>
</>
)}
</>
) : null}
)}
</Card>
<div className="mt-4 flex flex-row items-center gap-2">
<Dialog
onOpenChange={(open) => {
if (open) {
posthog.capture("project_settings:pricing_dialog_opened");
}
}}
>
<DialogTrigger asChild>
<Button variant="secondary">Change plans</Button>
</DialogTrigger>
<DialogContent className="max-w-3xl">
<DialogHeader>
<Header
title="Select plan"
level="h3"
actionButtons={
<Button variant="secondary" asChild>
<Link href="https://langfuse.com/pricing">
Pricing page
</Link>
</Button>
}
/>
</DialogHeader>
<p>
All plans offer a 7-day free trial. For more information about the
plans, please visit our pricing page or reach out to us via the
chat.
</p>
<PricingPage className="mb-5 mt-10 " />
</DialogContent>
</Dialog>
{plan === "Hobby" ? (
<Dialog
onOpenChange={(open) => {
if (open) {
posthog.capture("project_settings:pricing_dialog_opened");
}
}}
>
<DialogTrigger asChild>
<Button variant="secondary">Change plan</Button>
</DialogTrigger>
<DialogContent className="max-w-3xl">
<DialogHeader>
<Header
title="Select plan"
level="h3"
actionButtons={
<Button variant="secondary" asChild>
<Link href="https://langfuse.com/pricing">
Pricing page
</Link>
</Button>
}
/>
</DialogHeader>
<PricingPage className="mb-5 mt-5" />
</DialogContent>
</Dialog>
) : (
<Button variant="secondary">
<Link href="https://billing.stripe.com/p/login/6oE9BXd4u8PR2aYaEE">
Billing settings
</Link>
</Button>
)}
<div className="inline-block text-sm text-gray-500">
Currently: {plan}
Current plan: {plan}
</div>
</div>
</div>
@@ -5,23 +5,23 @@ import {
import * as z from "zod";
export const usageMeteringRouter = createTRPCRouter({
currentMonth: protectedProjectProcedure
last30d: protectedProjectProcedure
.input(
z.object({
projectId: z.string(),
}),
)
.query(async ({ input, ctx }) => {
const startOfThisMonth = new Date();
startOfThisMonth.setDate(1);
startOfThisMonth.setHours(0, 0, 0, 0);
const thirtyDaysAgo = new Date();
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
thirtyDaysAgo.setHours(0, 0, 0, 0);
const usage = await ctx.prisma.observation.count({
where: {
projectId: input.projectId,
startTime: {
gte: startOfThisMonth,
gte: thirtyDaysAgo,
},
},
});