Files
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

189 lines
6.6 KiB
Go

package workspace
import (
"context"
"database/sql"
"errors"
"fmt"
"time"
modelbindingbiz "github.com/tutti-os/tutti/services/tuttid/biz/modelbinding"
)
// ErrAgentModelBindingNotFound reports a missing binding row.
var ErrAgentModelBindingNotFound = errors.New("agent model binding not found")
// ErrAgentModelBindingReferenceInvalid reports a binding whose target, plan, or
// model usage policy does not exist (enforced by foreign keys at write time).
var ErrAgentModelBindingReferenceInvalid = errors.New("agent model binding reference is invalid")
func (s *SQLiteStore) ListAgentModelBindings(ctx context.Context, workspaceID string) ([]modelbindingbiz.Binding, error) {
if s == nil || s.readDB == nil {
return nil, errors.New("workspace database is not initialized")
}
rows, err := s.readDB.QueryContext(ctx, `
SELECT workspace_id, agent_target_id, model_plan_id, default_model, model_policy_id, updated_at_unix_ms
FROM agent_target_model_bindings
WHERE workspace_id = ?
ORDER BY agent_target_id ASC
`, workspaceID)
if err != nil {
return nil, fmt.Errorf("list agent model bindings: %w", err)
}
defer rows.Close()
var bindings []modelbindingbiz.Binding
for rows.Next() {
binding, err := scanAgentModelBinding(rows)
if err != nil {
return nil, err
}
bindings = append(bindings, binding)
}
if err := rows.Err(); err != nil {
return nil, fmt.Errorf("list agent model binding rows: %w", err)
}
return bindings, nil
}
func (s *SQLiteStore) GetAgentModelBinding(ctx context.Context, workspaceID string, agentTargetID string) (modelbindingbiz.Binding, error) {
if s == nil || s.readDB == nil {
return modelbindingbiz.Binding{}, errors.New("workspace database is not initialized")
}
row := s.readDB.QueryRowContext(ctx, `
SELECT workspace_id, agent_target_id, model_plan_id, default_model, model_policy_id, updated_at_unix_ms
FROM agent_target_model_bindings
WHERE workspace_id = ? AND agent_target_id = ?
`, workspaceID, agentTargetID)
binding, err := scanAgentModelBinding(row)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return modelbindingbiz.Binding{}, ErrAgentModelBindingNotFound
}
return modelbindingbiz.Binding{}, err
}
return binding, nil
}
func (s *SQLiteStore) PutAgentModelBinding(ctx context.Context, binding modelbindingbiz.Binding) error {
if s == nil || s.writeDB == nil {
return errors.New("workspace database is not initialized")
}
_, err := s.writeDB.ExecContext(ctx, `
INSERT INTO agent_target_model_bindings (
workspace_id, agent_target_id, model_plan_id, default_model, model_policy_id, updated_at_unix_ms
) VALUES (?, ?, ?, ?, ?, ?)
ON CONFLICT(workspace_id, agent_target_id) DO UPDATE SET
model_plan_id = excluded.model_plan_id,
default_model = excluded.default_model,
model_policy_id = excluded.model_policy_id,
updated_at_unix_ms = excluded.updated_at_unix_ms
`, binding.WorkspaceID, binding.AgentTargetID, nullableText(binding.ModelPlanID), binding.DefaultModel, nullableText(binding.ModelPolicyID), unixMs(binding.UpdatedAt))
if err != nil {
if isSQLiteForeignKeyConstraintError(err) {
return ErrAgentModelBindingReferenceInvalid
}
return fmt.Errorf("put agent model binding: %w", err)
}
return nil
}
// nullableText stores an empty optional link as SQL NULL so it is exempt from
// the composite foreign key while non-empty links are constrained.
func nullableText(value string) any {
if value == "" {
return nil
}
return value
}
func (s *SQLiteStore) DeleteAgentModelBinding(ctx context.Context, workspaceID string, agentTargetID string) error {
if s == nil || s.writeDB == nil {
return errors.New("workspace database is not initialized")
}
_, err := s.writeDB.ExecContext(ctx, `
DELETE FROM agent_target_model_bindings
WHERE workspace_id = ? AND agent_target_id = ?
`, workspaceID, agentTargetID)
if err != nil {
return fmt.Errorf("delete agent model binding: %w", err)
}
return nil
}
// ListAgentModelBindingsByPlan reports every binding referencing one plan so
// plan deletion can be blocked while consumers remain.
func (s *SQLiteStore) ListAgentModelBindingsByPlan(ctx context.Context, workspaceID string, planID string) ([]modelbindingbiz.Binding, error) {
if s == nil || s.readDB == nil {
return nil, errors.New("workspace database is not initialized")
}
rows, err := s.readDB.QueryContext(ctx, `
SELECT workspace_id, agent_target_id, model_plan_id, default_model, model_policy_id, updated_at_unix_ms
FROM agent_target_model_bindings
WHERE workspace_id = ? AND model_plan_id = ?
ORDER BY agent_target_id ASC
`, workspaceID, planID)
if err != nil {
return nil, fmt.Errorf("list agent model bindings by plan: %w", err)
}
defer rows.Close()
var bindings []modelbindingbiz.Binding
for rows.Next() {
binding, err := scanAgentModelBinding(rows)
if err != nil {
return nil, err
}
bindings = append(bindings, binding)
}
if err := rows.Err(); err != nil {
return nil, fmt.Errorf("list agent model bindings by plan rows: %w", err)
}
return bindings, nil
}
// ListAgentModelBindingsByModelPolicy reports every binding referencing one
// model usage policy so policy deletion can be blocked while consumers remain.
func (s *SQLiteStore) ListAgentModelBindingsByModelPolicy(ctx context.Context, workspaceID string, policyID string) ([]modelbindingbiz.Binding, error) {
if s == nil || s.readDB == nil {
return nil, errors.New("workspace database is not initialized")
}
rows, err := s.readDB.QueryContext(ctx, `
SELECT workspace_id, agent_target_id, model_plan_id, default_model, model_policy_id, updated_at_unix_ms
FROM agent_target_model_bindings
WHERE workspace_id = ? AND model_policy_id = ?
ORDER BY agent_target_id ASC
`, workspaceID, policyID)
if err != nil {
return nil, fmt.Errorf("list agent model bindings by model policy: %w", err)
}
defer rows.Close()
var bindings []modelbindingbiz.Binding
for rows.Next() {
binding, err := scanAgentModelBinding(rows)
if err != nil {
return nil, err
}
bindings = append(bindings, binding)
}
if err := rows.Err(); err != nil {
return nil, fmt.Errorf("list agent model bindings by model policy rows: %w", err)
}
return bindings, nil
}
func scanAgentModelBinding(row managedProviderScanner) (modelbindingbiz.Binding, error) {
var binding modelbindingbiz.Binding
var modelPlanID sql.NullString
var modelPolicyID sql.NullString
var updatedAtUnixMS int64
if err := row.Scan(&binding.WorkspaceID, &binding.AgentTargetID, &modelPlanID, &binding.DefaultModel, &modelPolicyID, &updatedAtUnixMS); err != nil {
return modelbindingbiz.Binding{}, err
}
binding.ModelPlanID = modelPlanID.String
binding.ModelPolicyID = modelPolicyID.String
binding.UpdatedAt = time.UnixMilli(updatedAtUnixMS).UTC()
return binding, nil
}