updates on the ai and agent and all thse things awith onboarding
This commit is contained in:
127
controllers/aiPlaygroundController.go
Normal file
127
controllers/aiPlaygroundController.go
Normal file
@@ -0,0 +1,127 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"doormile/db"
|
||||
"doormile/internal/ai/playground"
|
||||
"doormile/utils"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
// POST /admin/ai/playground/run — Agent Studio's Test tab. Runs one prompt
|
||||
// through the configured model with a registry skill's tools; see internal/ai/playground for
|
||||
// what executes and what only becomes a proposal. Staff only, roleid 1 only,
|
||||
// and rate-limited per user because every run is a paid API call.
|
||||
|
||||
// PlaygroundModel is the model client the playground uses (an OpenAI-compatible
|
||||
// provider such as Groq, see main.go). Nil until PLAYGROUND_LLM_API_KEY is set; the endpoint then answers 503 and the console keeps the
|
||||
// Test tab labelled as unavailable.
|
||||
var PlaygroundModel playground.Model
|
||||
|
||||
const (
|
||||
playgroundRunTimeout = 120 * time.Second
|
||||
playgroundRunsPerWin = 10
|
||||
playgroundWindow = 10 * time.Minute
|
||||
)
|
||||
|
||||
type playgroundLimiter struct {
|
||||
mu sync.Mutex
|
||||
runs map[string][]time.Time
|
||||
}
|
||||
|
||||
var playgroundRuns = &playgroundLimiter{runs: map[string][]time.Time{}}
|
||||
|
||||
// allow records a run for key and reports whether it is within the limit.
|
||||
func (l *playgroundLimiter) allow(key string, now time.Time) bool {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
kept := l.runs[key][:0]
|
||||
for _, t := range l.runs[key] {
|
||||
if now.Sub(t) < playgroundWindow {
|
||||
kept = append(kept, t)
|
||||
}
|
||||
}
|
||||
if len(kept) >= playgroundRunsPerWin {
|
||||
l.runs[key] = kept
|
||||
return false
|
||||
}
|
||||
l.runs[key] = append(kept, now)
|
||||
return true
|
||||
}
|
||||
|
||||
// RunAIPlayground — POST /admin/ai/playground/run {agentid, skillid?, prompt}
|
||||
func RunAIPlayground(c *fiber.Ctx) error {
|
||||
if PlaygroundModel == nil {
|
||||
return utils.Fail(c, fiber.StatusServiceUnavailable, "PLAYGROUND_NOT_CONFIGURED",
|
||||
"The Test playground has no model configured on this server.")
|
||||
}
|
||||
|
||||
var body struct {
|
||||
Agentid string `json:"agentid"`
|
||||
Skillid string `json:"skillid"`
|
||||
Prompt string `json:"prompt"`
|
||||
}
|
||||
if err := c.BodyParser(&body); err != nil {
|
||||
return utils.BadRequest(c, "invalid request body")
|
||||
}
|
||||
body.Prompt = strings.TrimSpace(body.Prompt)
|
||||
if body.Agentid == "" || body.Prompt == "" {
|
||||
return utils.BadRequest(c, "agentid and prompt are required")
|
||||
}
|
||||
if utf8.RuneCountInString(body.Prompt) > playground.MaxPromptChars {
|
||||
return utils.BadRequest(c, "prompt is too long (at most 2000 characters)")
|
||||
}
|
||||
|
||||
actor := actorOf(c)
|
||||
key := actor.Email
|
||||
if key == "" {
|
||||
key = "user:" + strconv.Itoa(actor.UserID)
|
||||
}
|
||||
if !playgroundRuns.allow(key, time.Now()) {
|
||||
return utils.Fail(c, fiber.StatusTooManyRequests, "PLAYGROUND_RATE_LIMITED",
|
||||
"Playground limit reached: 10 runs per 10 minutes. Try again shortly.")
|
||||
}
|
||||
|
||||
snap, ok := loadRegistry(c)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
plan, err := playground.Prepare(snap, body.Agentid, body.Skillid)
|
||||
if errors.Is(err, playground.ErrNotFound) {
|
||||
return utils.NotFound(c, err.Error())
|
||||
}
|
||||
if err != nil {
|
||||
return utils.BadRequest(c, err.Error())
|
||||
}
|
||||
|
||||
// An OpenAI-compatible provider serves its own configured model, not the
|
||||
// agent's registry pin (a Claude id AI_engine uses); report the real one.
|
||||
if named, ok := PlaygroundModel.(interface{ ModelName() string }); ok {
|
||||
plan.Model = named.ModelName()
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), playgroundRunTimeout)
|
||||
defer cancel()
|
||||
trace, err := playground.Run(ctx, PlaygroundModel, plan, body.Prompt, playground.Executors(db.DB, db.Rdb))
|
||||
utils.Info("ai playground run", "email", actor.Email, "agent", plan.AgentID, "skill", plan.SkillID,
|
||||
"model", plan.Model, "turns", trace.Turns, "ms", trace.Ms, "failed", err != nil)
|
||||
if err != nil {
|
||||
utils.Error("ai playground: model call failed", "error", err.Error())
|
||||
var pe *playground.ProviderError
|
||||
if errors.As(err, &pe) && pe.Status == fiber.StatusTooManyRequests {
|
||||
return utils.Fail(c, fiber.StatusTooManyRequests, "PLAYGROUND_PROVIDER_RATE_LIMITED",
|
||||
"The model provider's rate limit was reached (common on free plans). Wait a minute and try again.")
|
||||
}
|
||||
return utils.Fail(c, fiber.StatusBadGateway, "PLAYGROUND_MODEL_FAILED",
|
||||
"The model request failed; nothing was changed. Try again.")
|
||||
}
|
||||
return utils.OK(c, trace)
|
||||
}
|
||||
Reference in New Issue
Block a user