Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: team pricing #583

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@

# 📈 lunary

**Open-source observability & prompt platform for LLMs**
**Open-source observability, prompt management & evaluations for LLMs**

[website](https://lunary.ai) - [docs](https://lunary.ai/docs) - [self host](https://lunary.ai/docs/self-host)

[![npm version](https://badge.fury.io/js/lunary.svg)](https://badge.fury.io/js/lunary) ![PyPI - Version](https://img.shields.io/pypi/v/llmonitor) ![GitHub last commit (by committer)](https://img.shields.io/github/last-commit/lunary-ai/lunary) ![GitHub commit activity (branch)](https://img.shields.io/github/commit-activity/w/lunary-ai/lunary)
[![npm version](https://badge.fury.io/js/lunary.svg)](https://badge.fury.io/js/lunary) ![PyPI - Version](https://img.shields.io/pypi/v/lunary) ![GitHub last commit (by committer)](https://img.shields.io/github/last-commit/lunary-ai/lunary) ![GitHub commit activity (branch)](https://img.shields.io/github/commit-activity/w/lunary-ai/lunary)

</div>

## Features

Lunary helps AI devs take their apps in production, with features such as:
Lunary helps LLM developers take their apps to the next level.

- 💵 Analytics (cost, token, latency, ..)
- 🔍 Monitoring (logs, traces, user tracking, ..)
Expand All @@ -25,9 +25,9 @@ It also designed to be:

- 🤖 Usable with any model, not just OpenAI
- 📦 Easy to integrate (2 minutes)
- 🧑‍💻 Simple to self-host
- 🧑‍💻 Self-hostable

## Demo
## 1-min Demo

https://github.com/lunary-ai/lunary/assets/5092466/a2b4ba9b-4afb-46e3-9b6b-faf7ddb4a931

Expand All @@ -44,15 +44,15 @@ Lunary natively supports:
- [OpenAI module](https://lunary.ai/docs/js/openai)
- [LiteLLM](https://docs.litellm.ai/docs/observability/lunary_integration)

Additionally you can use it with any framework by wrapping the relevant methods.
Additionally you can use it with any other LLM by manually sending events.

## 📚 Documentation

Full documentation is available [on the website](https://lunary.ai/docs/intro).

## ☁️ Hosted version

We offer [a hosted version](https://lunary.ai) with a free plan of up to 1k requests / days.
We offer [a hosted version](https://lunary.ai) with a free plan of up to 10k requests / month.

With the hosted version:

Expand All @@ -75,7 +75,7 @@ When using our JS or Python SDK, you need to set the environment variable `LUNAR

## 🙋 Support

Need help or have questions? Chat with us on [the website](https://lunary.ai) or email us: [hello [at] lunary.ai](mailto:[email protected]). We're here to support you every step of the way.
Need help or have questions? Chat with us on [the website](https://lunary.ai) or email us: [hello [at] lunary.ai](mailto:[email protected]). We're here to help every step of the way.

## License

Expand Down
32 changes: 16 additions & 16 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions packages/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
"bcrypt": "^5.1.1",
"jose": "^5.2.0",
"js-tiktoken": "1.0.7",
"jsonrepair": "^3.6.0",
"koa": "^2.15.0",
"jsonrepair": "^3.7.0",
"koa": "^2.15.3",
"koa-bodyparser": "^4.4.1",
"koa-logger": "^3.2.1",
"koa-ratelimit": "^5.1.0",
Expand All @@ -39,9 +39,9 @@
"rouge": "^1.0.3",
"samlify": "^2.8.11",
"shared": "*",
"stripe": "^14.14.0",
"stripe": "^15.5.0",
"washyourmouthoutwithsoap": "^1.0.2",
"zod": "^3.22.4"
"zod": "^3.23.6"
},
"devDependencies": {
"@types/bcrypt": "^5.0.2",
Expand Down
1 change: 1 addition & 0 deletions packages/backend/src/api/v1/evaluations/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ evaluations.post(
provider,
prompt: prompt.messages,
checklistId,
orgId,
})
console.log(`Task ${count} don with model ${provider.model} done`)
})
Expand Down
4 changes: 4 additions & 0 deletions packages/backend/src/api/v1/evaluations/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ interface RunEvalParams {
provider: Provider
prompt: any
variation: any
orgId: string
}

export async function runEval({
Expand All @@ -20,6 +21,7 @@ export async function runEval({
provider,
prompt,
variation,
orgId,
}: RunEvalParams) {
try {
console.log(`=============================`)
Expand Down Expand Up @@ -53,6 +55,8 @@ export async function runEval({
provider.config,
undefined,
provider.model,
false,
orgId,
)
break // Break the loop if the call was successful
} catch (error) {
Expand Down
16 changes: 16 additions & 0 deletions packages/backend/src/api/v1/filters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,20 @@ filters.get("/radars", async (ctx) => {
ctx.body = rows
})

filters.get("/templates", async (ctx) => {
const { projectId } = ctx.state

const rows = await sql`
select
id as value,
slug as label
from
template
where
project_id = ${projectId}
`

ctx.body = rows
})

export default filters
2 changes: 1 addition & 1 deletion packages/backend/src/api/v1/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import filters from "./filters"
import projects from "./projects"
import radars from "./radars"
import runs from "./runs/index"
import templateVersions from "./templateVersions"
import templateVersions from "./template-versions"
import templates from "./templates"
import users from "./users"

Expand Down
56 changes: 27 additions & 29 deletions packages/backend/src/api/v1/orgs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,23 +102,23 @@ orgs.get("/billing-portal", async (ctx: Context) => {
orgs.post("/upgrade", async (ctx: Context) => {
const orgId = ctx.state.orgId as string

const { plan, period, origin } = ctx.request.body as {
plan: string
period: string
const { origin } = ctx.request.body as {
// plan: string
// period: string
origin: string
}

const lookupKey = `${plan}_${period}`
const plan = "team"

const prices = await stripe.prices.list({
lookup_keys: [lookupKey],
})
const lookupKeys = [`team_seats`, `team_runs`, `team_ai_playground`]

const prices = await stripe.prices.list({ lookup_keys: lookupKeys })

if (prices.data.length === 0) {
throw new Error("No price found for this plan and period")
if (prices.length < 3) {
throw new Error("Prices not all found")
}

const priceId = prices.data[0].id as string
// const priceId = prices.data[0].id as string

const [org] = await sql`
select
Expand All @@ -134,6 +134,14 @@ orgs.post("/upgrade", async (ctx: Context) => {

if (!org) throw new Error("Org not found")

const seats =
await sql`select count(*) as count from account where org_id = ${orgId}`

const newItems = prices.data.map((price) => ({
price: price.id,
quantity: price.lookup_key === "team_seats" ? seats[0].count : undefined,
}))

if (!org.stripe_subscription) {
const checkoutSession = await stripe.checkout.sessions.create({
mode: "subscription",
Expand All @@ -142,39 +150,29 @@ orgs.post("/upgrade", async (ctx: Context) => {
customer: org.stripeCustomer || undefined,
metadata: {
plan,
period,
// period,
},
line_items: [
{
price: priceId,
quantity: 1,
},
],
line_items: newItems,
success_url: `${origin}/billing/thank-you`,
cancel_url: `${origin}/billing`,
})

return (ctx.body = { ok: true, url: checkoutSession.url })
} else {
const subscription = await stripe.subscriptions.retrieve(
org.stripeSubscription,
)
// const subscription = await stripe.subscriptions.retrieve(
// org.stripeSubscription,
// )

const subItem = subscription.items.data[0].id
// const subItem = subscription.items.data[0].id

// Update user subscription with new price
await stripe.subscriptions.update(org.stripeSubscription, {
cancel_at_period_end: false,
metadata: {
plan,
period,
// period,
},
items: [
{
id: subItem,
price: priceId,
},
],
items: newItems,
})

// Update org plan
Expand Down Expand Up @@ -225,7 +223,7 @@ orgs.post("/playground", async (ctx: Context) => {
`
const model = extra?.model || "gpt-3.5-turbo"

const res = await runAImodel(content, extra, variables, model, true)
const res = await runAImodel(content, extra, variables, model, true, orgId)

const stream = new PassThrough()
stream.pipe(ctx.res)
Expand Down
5 changes: 4 additions & 1 deletion packages/backend/src/api/v1/runs/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { Feedback, deserializeLogic } from "shared"
import { convertChecksToSQL } from "@/src/utils/checks"
import { checkAccess } from "@/src/utils/authorization"
import { jsonrepair } from "jsonrepair"
import { z } from "zod"

const runs = new Router({
prefix: "/runs",
Expand Down Expand Up @@ -84,6 +83,7 @@ const formatRun = (run: any) => ({
endedAt: run.endedAt,
duration: run.duration,
templateVersionId: run.templateVersionId,
templateSlug: run.templateSlug,
cost: run.cost,
tokens: {
completion: run.completionTokens,
Expand Down Expand Up @@ -141,11 +141,14 @@ runs.get("/", async (ctx: Context) => {
eu.created_at as user_created_at,
eu.last_seen as user_last_seen,
eu.props as user_props,
t.slug as template_slug,
rpfc.feedback as parent_feedback
from
run r
left join external_user eu on r.external_user_id = eu.id
left join run_parent_feedback_cache rpfc ON r.id = rpfc.id
left join template_version tv on r.template_version_id = tv.id
left join template t on tv.template_id = t.id
where
r.project_id = ${projectId}
${parentRunCheck}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ versions.get("/latest", async (ctx: Context) => {
ctx.throw("Template not found, is the project ID correct?", 404)
}

latestVersion.extra = unCamelObject(latestVersion.extra)
latestVersion.content = latestVersion.content.map((c: any) =>
unCamelObject(c),
)

ctx.body = latestVersion
})

Expand Down
5 changes: 4 additions & 1 deletion packages/backend/src/api/v1/users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,15 +277,18 @@ users.patch(

const [{ plan }] =
await sql`select plan, eval_allowance from org where id = ${orgId}`
if (plan === "free" || plan === "pro") {

if (["free", "pro", "team", "unlimited"].includes(plan)) {
ctx.throw(403, "You must be an enterprise customer to change a user role")
}

if (role === "owner") {
ctx.throw(403, "You cannot modify the owner role")
}

const [currentUser] =
await sql`select * from account where id = ${currentUserId}`

if (!["owner", "admin"].includes(currentUser.role)) {
ctx.throw(403, "You do not have permission to modify this user")
}
Expand Down
2 changes: 1 addition & 1 deletion packages/backend/src/api/webhooks/stripe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const setupSubscription = async (object: Stripe.Checkout.Session) => {
throw new Error("client_reference_id is missing")
}

const plan = metadata?.plan || "pro"
const plan = metadata?.plan || "team"
const period = metadata?.period || "monthly"

const orgData = {
Expand Down
Loading
Loading