Add database models and infrastructure for AI operation tracking and organization-level AI configuration. OperationLog model tracks all AI operations for auditing, including: - Operation type, tier, and trigger event - Token usage (input/output) - Status tracking (pending, success, failed, escalated) - Performance metrics (duration) - Rate limiting support via CountRecentOperations OrgAISettings model stores per-organization AI configuration: - Provider and model selection - Encrypted API key storage - Rate limits (max operations per hour) - Allowed operations whitelist - Agent mode permissions Also adds AI unit type to repository units for enabling/disabling AI features per repo.
113 lines
3.5 KiB
Go
113 lines
3.5 KiB
Go
// Copyright 2026 MarketAlly. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package ai
|
|
|
|
import (
|
|
"context"
|
|
|
|
"code.gitcaddy.com/server/v3/models/db"
|
|
"code.gitcaddy.com/server/v3/modules/timeutil"
|
|
|
|
"xorm.io/builder"
|
|
)
|
|
|
|
func init() {
|
|
db.RegisterModel(new(OperationLog))
|
|
}
|
|
|
|
// OperationLog records every AI operation for auditing
|
|
type OperationLog struct {
|
|
ID int64 `xorm:"pk autoincr"`
|
|
RepoID int64 `xorm:"INDEX NOT NULL"`
|
|
Operation string `xorm:"VARCHAR(50) NOT NULL"` // "code-review", "issue-response", etc.
|
|
Tier int `xorm:"NOT NULL"` // 1 or 2
|
|
TriggerEvent string `xorm:"VARCHAR(100) NOT NULL"`
|
|
TriggerUserID int64 `xorm:"INDEX"`
|
|
TargetID int64 `xorm:"INDEX"` // issue/PR ID
|
|
TargetType string `xorm:"VARCHAR(20)"` // "issue", "pull", "commit"
|
|
Provider string `xorm:"VARCHAR(20)"`
|
|
Model string `xorm:"VARCHAR(100)"`
|
|
InputTokens int `xorm:"DEFAULT 0"`
|
|
OutputTokens int `xorm:"DEFAULT 0"`
|
|
Status string `xorm:"VARCHAR(20) NOT NULL"` // "success", "failed", "escalated", "pending"
|
|
ResultCommentID int64 `xorm:"DEFAULT 0"`
|
|
ActionRunID int64 `xorm:"DEFAULT 0"` // for Tier 2
|
|
ErrorMessage string `xorm:"TEXT"`
|
|
DurationMs int64 `xorm:"DEFAULT 0"`
|
|
CreatedUnix timeutil.TimeStamp `xorm:"INDEX CREATED"`
|
|
}
|
|
|
|
// TableName returns the table name for OperationLog
|
|
func (OperationLog) TableName() string {
|
|
return "ai_operation_log"
|
|
}
|
|
|
|
// OperationStatus constants
|
|
const (
|
|
OperationStatusPending = "pending"
|
|
OperationStatusSuccess = "success"
|
|
OperationStatusFailed = "failed"
|
|
OperationStatusEscalated = "escalated"
|
|
)
|
|
|
|
// InsertOperationLog creates a new operation log entry
|
|
func InsertOperationLog(ctx context.Context, log *OperationLog) error {
|
|
return db.Insert(ctx, log)
|
|
}
|
|
|
|
// UpdateOperationLog updates an existing operation log entry
|
|
func UpdateOperationLog(ctx context.Context, log *OperationLog) error {
|
|
_, err := db.GetEngine(ctx).ID(log.ID).AllCols().Update(log)
|
|
return err
|
|
}
|
|
|
|
// GetOperationLog returns a single operation log entry by ID
|
|
func GetOperationLog(ctx context.Context, id int64) (*OperationLog, error) {
|
|
log := &OperationLog{}
|
|
has, err := db.GetEngine(ctx).ID(id).Get(log)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !has {
|
|
return nil, nil
|
|
}
|
|
return log, nil
|
|
}
|
|
|
|
// FindOperationLogsOptions represents options for finding operation logs
|
|
type FindOperationLogsOptions struct {
|
|
db.ListOptions
|
|
RepoID int64
|
|
Operation string
|
|
Status string
|
|
Tier int
|
|
}
|
|
|
|
func (opts FindOperationLogsOptions) ToConds() builder.Cond {
|
|
cond := builder.NewCond()
|
|
if opts.RepoID > 0 {
|
|
cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
|
|
}
|
|
if opts.Operation != "" {
|
|
cond = cond.And(builder.Eq{"operation": opts.Operation})
|
|
}
|
|
if opts.Status != "" {
|
|
cond = cond.And(builder.Eq{"status": opts.Status})
|
|
}
|
|
if opts.Tier > 0 {
|
|
cond = cond.And(builder.Eq{"tier": opts.Tier})
|
|
}
|
|
return cond
|
|
}
|
|
|
|
func (opts FindOperationLogsOptions) ToOrders() string {
|
|
return "created_unix DESC"
|
|
}
|
|
|
|
// CountRecentOperations counts operations in the last hour for rate limiting
|
|
func CountRecentOperations(ctx context.Context, repoID int64) (int64, error) {
|
|
oneHourAgo := timeutil.TimeStampNow() - 3600
|
|
return db.GetEngine(ctx).Where("repo_id = ? AND created_unix > ?", repoID, oneHourAgo).Count(new(OperationLog))
|
|
}
|