mirror of
https://github.com/mudler/LocalAI.git
synced 2026-03-31 13:15:51 -04:00
* feat: add distributed mode (experimental) Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * fix data races, mutexes, transactions Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * refactorings Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * fixups Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * fix events and tool stream in agent chat Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * use ginkgo Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * refactoring and consolidation Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * refactoring and consolidation Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * refactoring and consolidation Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * refactoring and consolidation Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * refactoring and consolidation Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * refactoring and consolidation Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * refactoring and consolidation Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * refactoring and consolidation Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * fix(cron): compute correctly time boundaries avoiding re-triggering Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * enhancements, refactorings Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * do not flood of healthy checks Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * do not list obvious backends as text backends Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * tests fixups Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * refactoring and consolidation Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * Drop redundant healthcheck Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * enhancements, refactorings Signed-off-by: Ettore Di Giacinto <mudler@localai.io> --------- Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
162 lines
4.1 KiB
Go
162 lines
4.1 KiB
Go
//go:build auth
|
|
|
|
package auth_test
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/mudler/LocalAI/core/http/auth"
|
|
. "github.com/onsi/ginkgo/v2"
|
|
. "github.com/onsi/gomega"
|
|
)
|
|
|
|
var _ = Describe("Usage", func() {
|
|
Describe("RecordUsage", func() {
|
|
It("inserts a usage record", func() {
|
|
db := testDB()
|
|
record := &auth.UsageRecord{
|
|
UserID: "user-1",
|
|
UserName: "Test User",
|
|
Model: "gpt-4",
|
|
Endpoint: "/v1/chat/completions",
|
|
PromptTokens: 100,
|
|
CompletionTokens: 50,
|
|
TotalTokens: 150,
|
|
Duration: 1200,
|
|
CreatedAt: time.Now(),
|
|
}
|
|
err := auth.RecordUsage(db, record)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(record.ID).ToNot(BeZero())
|
|
})
|
|
})
|
|
|
|
Describe("GetUserUsage", func() {
|
|
It("returns aggregated usage for a specific user", func() {
|
|
db := testDB()
|
|
|
|
// Insert records for two users
|
|
for range 3 {
|
|
err := auth.RecordUsage(db, &auth.UsageRecord{
|
|
UserID: "user-a",
|
|
UserName: "Alice",
|
|
Model: "gpt-4",
|
|
Endpoint: "/v1/chat/completions",
|
|
PromptTokens: 100,
|
|
TotalTokens: 150,
|
|
CreatedAt: time.Now(),
|
|
})
|
|
Expect(err).ToNot(HaveOccurred())
|
|
}
|
|
err := auth.RecordUsage(db, &auth.UsageRecord{
|
|
UserID: "user-b",
|
|
UserName: "Bob",
|
|
Model: "gpt-4",
|
|
PromptTokens: 200,
|
|
TotalTokens: 300,
|
|
CreatedAt: time.Now(),
|
|
})
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
buckets, err := auth.GetUserUsage(db, "user-a", "month")
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(buckets).ToNot(BeEmpty())
|
|
|
|
// All returned buckets should be for user-a's model
|
|
totalPrompt := int64(0)
|
|
for _, b := range buckets {
|
|
totalPrompt += b.PromptTokens
|
|
}
|
|
Expect(totalPrompt).To(Equal(int64(300)))
|
|
})
|
|
|
|
It("filters by period", func() {
|
|
db := testDB()
|
|
|
|
// Record in the past (beyond day window)
|
|
err := auth.RecordUsage(db, &auth.UsageRecord{
|
|
UserID: "user-c",
|
|
UserName: "Carol",
|
|
Model: "gpt-4",
|
|
PromptTokens: 100,
|
|
TotalTokens: 100,
|
|
CreatedAt: time.Now().Add(-48 * time.Hour),
|
|
})
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
// Record now
|
|
err = auth.RecordUsage(db, &auth.UsageRecord{
|
|
UserID: "user-c",
|
|
UserName: "Carol",
|
|
Model: "gpt-4",
|
|
PromptTokens: 200,
|
|
TotalTokens: 200,
|
|
CreatedAt: time.Now(),
|
|
})
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
// Day period should only include recent record
|
|
buckets, err := auth.GetUserUsage(db, "user-c", "day")
|
|
Expect(err).ToNot(HaveOccurred())
|
|
totalPrompt := int64(0)
|
|
for _, b := range buckets {
|
|
totalPrompt += b.PromptTokens
|
|
}
|
|
Expect(totalPrompt).To(Equal(int64(200)))
|
|
|
|
// Month period should include both
|
|
buckets, err = auth.GetUserUsage(db, "user-c", "month")
|
|
Expect(err).ToNot(HaveOccurred())
|
|
totalPrompt = 0
|
|
for _, b := range buckets {
|
|
totalPrompt += b.PromptTokens
|
|
}
|
|
Expect(totalPrompt).To(Equal(int64(300)))
|
|
})
|
|
})
|
|
|
|
Describe("GetAllUsage", func() {
|
|
It("returns usage for all users", func() {
|
|
db := testDB()
|
|
|
|
for _, uid := range []string{"user-x", "user-y"} {
|
|
err := auth.RecordUsage(db, &auth.UsageRecord{
|
|
UserID: uid,
|
|
UserName: uid,
|
|
Model: "gpt-4",
|
|
PromptTokens: 100,
|
|
TotalTokens: 150,
|
|
CreatedAt: time.Now(),
|
|
})
|
|
Expect(err).ToNot(HaveOccurred())
|
|
}
|
|
|
|
buckets, err := auth.GetAllUsage(db, "month", "")
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(len(buckets)).To(BeNumerically(">=", 2))
|
|
})
|
|
|
|
It("filters by user ID when specified", func() {
|
|
db := testDB()
|
|
|
|
err := auth.RecordUsage(db, &auth.UsageRecord{
|
|
UserID: "user-p", UserName: "Pat", Model: "gpt-4",
|
|
PromptTokens: 100, TotalTokens: 100, CreatedAt: time.Now(),
|
|
})
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
err = auth.RecordUsage(db, &auth.UsageRecord{
|
|
UserID: "user-q", UserName: "Quinn", Model: "gpt-4",
|
|
PromptTokens: 200, TotalTokens: 200, CreatedAt: time.Now(),
|
|
})
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
buckets, err := auth.GetAllUsage(db, "month", "user-p")
|
|
Expect(err).ToNot(HaveOccurred())
|
|
for _, b := range buckets {
|
|
Expect(b.UserID).To(Equal("user-p"))
|
|
}
|
|
})
|
|
})
|
|
})
|