mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-08-31 01:13:06 +08:00
Add composite group route registry
This commit is contained in:
@@ -135,9 +135,11 @@ func initializeApplication(buildInfo handler.BuildInfo) (*Application, error) {
|
||||
channelRepository := repository.NewChannelRepository(db)
|
||||
channelService := service.NewChannelService(channelRepository, groupRepository, apiKeyAuthCacheInvalidator, pricingService)
|
||||
modelPricingResolver := service.NewModelPricingResolver(channelService, billingService)
|
||||
compositeModelRouteRepository := repository.NewCompositeModelRouteRepository(client)
|
||||
compositeRouteResolver := service.NewCompositeRouteResolver(compositeModelRouteRepository)
|
||||
notificationEmailService := service.NewNotificationEmailService(settingRepository, emailService)
|
||||
balanceNotifyService := service.ProvideBalanceNotifyService(emailService, settingRepository, accountRepository, notificationEmailService)
|
||||
gatewayService := service.NewGatewayService(accountRepository, groupRepository, usageLogRepository, usageBillingRepository, userRepository, userSubscriptionRepository, userGroupRateRepository, gatewayCache, configConfig, schedulerSnapshotService, concurrencyService, billingService, rateLimitService, billingCacheService, identityService, httpUpstream, deferredService, claudeTokenProvider, sessionLimitCache, rpmCache, digestSessionStore, settingService, tlsFingerprintProfileService, channelService, modelPricingResolver, balanceNotifyService, serviceUserPlatformQuotaRepository)
|
||||
gatewayService := service.NewGatewayService(accountRepository, groupRepository, usageLogRepository, usageBillingRepository, userRepository, userSubscriptionRepository, userGroupRateRepository, gatewayCache, configConfig, schedulerSnapshotService, concurrencyService, billingService, rateLimitService, billingCacheService, identityService, httpUpstream, deferredService, claudeTokenProvider, sessionLimitCache, rpmCache, digestSessionStore, settingService, tlsFingerprintProfileService, channelService, modelPricingResolver, compositeRouteResolver, balanceNotifyService, serviceUserPlatformQuotaRepository)
|
||||
openAIOAuthClient := repository.NewOpenAIOAuthClient()
|
||||
privacyClientFactory := providePrivacyClientFactory()
|
||||
openAIOAuthService := service.ProvideOpenAIOAuthService(proxyRepository, openAIOAuthClient, privacyClientFactory)
|
||||
@@ -180,7 +182,7 @@ func initializeApplication(buildInfo handler.BuildInfo) (*Application, error) {
|
||||
adminAccountRepository := repository.NewAdminAccountRepository(client, db, schedulerCache)
|
||||
proxyExitInfoProber := repository.NewProxyExitInfoProber(configConfig)
|
||||
proxyLatencyCache := repository.NewProxyLatencyCache(redisClient)
|
||||
adminService := service.NewAdminService(userRepository, adminGroupRepository, adminAccountRepository, proxyRepository, apiKeyRepository, redeemCodeRepository, userGroupRateRepository, userRPMCache, billingCacheService, proxyExitInfoProber, proxyLatencyCache, apiKeyAuthCacheInvalidator, client, settingService, subscriptionService, userSubscriptionRepository, privacyClientFactory, openAIGatewayService, affiliateService)
|
||||
adminService := service.NewAdminService(userRepository, adminGroupRepository, adminAccountRepository, proxyRepository, apiKeyRepository, redeemCodeRepository, userGroupRateRepository, userRPMCache, billingCacheService, proxyExitInfoProber, proxyLatencyCache, apiKeyAuthCacheInvalidator, client, settingService, subscriptionService, userSubscriptionRepository, privacyClientFactory, openAIGatewayService, affiliateService, compositeModelRouteRepository, compositeRouteResolver)
|
||||
adminUserHandler := admin.NewUserHandler(adminService, concurrencyService, serviceUserPlatformQuotaRepository, billingCache, totpService, userService, settingService)
|
||||
groupCapacityService := service.NewGroupCapacityService(accountRepository, groupRepository, concurrencyService, sessionLimitCache, rpmCache)
|
||||
groupHandler := admin.NewGroupHandler(adminService, dashboardService, groupCapacityService)
|
||||
@@ -299,7 +301,7 @@ func initializeApplication(buildInfo handler.BuildInfo) (*Application, error) {
|
||||
apiKeyAuthMiddleware := middleware.NewAPIKeyAuthMiddleware(apiKeyService, subscriptionService, configConfig)
|
||||
auditLogMiddleware := middleware.NewAuditLogMiddleware(auditLogService)
|
||||
stepUpAuthMiddleware := middleware.NewStepUpAuthMiddleware(totpService, userService, settingService)
|
||||
engine := server.ProvideRouter(configConfig, handlers, jwtAuthMiddleware, adminAuthMiddleware, apiKeyAuthMiddleware, auditLogMiddleware, stepUpAuthMiddleware, apiKeyService, subscriptionService, opsService, settingService, redisClient)
|
||||
engine := server.ProvideRouter(configConfig, handlers, jwtAuthMiddleware, adminAuthMiddleware, apiKeyAuthMiddleware, auditLogMiddleware, stepUpAuthMiddleware, apiKeyService, subscriptionService, opsService, settingService, compositeRouteResolver, redisClient)
|
||||
httpServer := server.ProvideHTTPServer(configConfig, engine)
|
||||
opsMetricsCollector := service.ProvideOpsMetricsCollector(opsRepository, settingRepository, accountRepository, concurrencyService, db, redisClient, configConfig)
|
||||
opsAggregationService := service.ProvideOpsAggregationService(opsRepository, settingRepository, db, redisClient, configConfig)
|
||||
|
||||
+175
-14
@@ -29,6 +29,7 @@ import (
|
||||
"github.com/Wei-Shaw/sub2api/ent/channelmonitordailyrollup"
|
||||
"github.com/Wei-Shaw/sub2api/ent/channelmonitorhistory"
|
||||
"github.com/Wei-Shaw/sub2api/ent/channelmonitorrequesttemplate"
|
||||
"github.com/Wei-Shaw/sub2api/ent/compositemodelroute"
|
||||
"github.com/Wei-Shaw/sub2api/ent/errorpassthroughrule"
|
||||
"github.com/Wei-Shaw/sub2api/ent/group"
|
||||
"github.com/Wei-Shaw/sub2api/ent/idempotencyrecord"
|
||||
@@ -90,6 +91,8 @@ type Client struct {
|
||||
ChannelMonitorHistory *ChannelMonitorHistoryClient
|
||||
// ChannelMonitorRequestTemplate is the client for interacting with the ChannelMonitorRequestTemplate builders.
|
||||
ChannelMonitorRequestTemplate *ChannelMonitorRequestTemplateClient
|
||||
// CompositeModelRoute is the client for interacting with the CompositeModelRoute builders.
|
||||
CompositeModelRoute *CompositeModelRouteClient
|
||||
// ErrorPassthroughRule is the client for interacting with the ErrorPassthroughRule builders.
|
||||
ErrorPassthroughRule *ErrorPassthroughRuleClient
|
||||
// Group is the client for interacting with the Group builders.
|
||||
@@ -163,6 +166,7 @@ func (c *Client) init() {
|
||||
c.ChannelMonitorDailyRollup = NewChannelMonitorDailyRollupClient(c.config)
|
||||
c.ChannelMonitorHistory = NewChannelMonitorHistoryClient(c.config)
|
||||
c.ChannelMonitorRequestTemplate = NewChannelMonitorRequestTemplateClient(c.config)
|
||||
c.CompositeModelRoute = NewCompositeModelRouteClient(c.config)
|
||||
c.ErrorPassthroughRule = NewErrorPassthroughRuleClient(c.config)
|
||||
c.Group = NewGroupClient(c.config)
|
||||
c.IdempotencyRecord = NewIdempotencyRecordClient(c.config)
|
||||
@@ -293,6 +297,7 @@ func (c *Client) Tx(ctx context.Context) (*Tx, error) {
|
||||
ChannelMonitorDailyRollup: NewChannelMonitorDailyRollupClient(cfg),
|
||||
ChannelMonitorHistory: NewChannelMonitorHistoryClient(cfg),
|
||||
ChannelMonitorRequestTemplate: NewChannelMonitorRequestTemplateClient(cfg),
|
||||
CompositeModelRoute: NewCompositeModelRouteClient(cfg),
|
||||
ErrorPassthroughRule: NewErrorPassthroughRuleClient(cfg),
|
||||
Group: NewGroupClient(cfg),
|
||||
IdempotencyRecord: NewIdempotencyRecordClient(cfg),
|
||||
@@ -350,6 +355,7 @@ func (c *Client) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error)
|
||||
ChannelMonitorDailyRollup: NewChannelMonitorDailyRollupClient(cfg),
|
||||
ChannelMonitorHistory: NewChannelMonitorHistoryClient(cfg),
|
||||
ChannelMonitorRequestTemplate: NewChannelMonitorRequestTemplateClient(cfg),
|
||||
CompositeModelRoute: NewCompositeModelRouteClient(cfg),
|
||||
ErrorPassthroughRule: NewErrorPassthroughRuleClient(cfg),
|
||||
Group: NewGroupClient(cfg),
|
||||
IdempotencyRecord: NewIdempotencyRecordClient(cfg),
|
||||
@@ -407,7 +413,7 @@ func (c *Client) Use(hooks ...Hook) {
|
||||
c.AuthIdentity, c.AuthIdentityChannel, c.BatchImageEvent, c.BatchImageItem,
|
||||
c.BatchImageJob, c.ChannelMonitor, c.ChannelMonitorDailyRollup,
|
||||
c.ChannelMonitorHistory, c.ChannelMonitorRequestTemplate,
|
||||
c.ErrorPassthroughRule, c.Group, c.IdempotencyRecord,
|
||||
c.CompositeModelRoute, c.ErrorPassthroughRule, c.Group, c.IdempotencyRecord,
|
||||
c.IdentityAdoptionDecision, c.PaymentAuditLog, c.PaymentOrder,
|
||||
c.PaymentProviderInstance, c.PendingAuthSession, c.PromoCode, c.PromoCodeUsage,
|
||||
c.Proxy, c.RedeemCode, c.SecuritySecret, c.Setting, c.SubscriptionPlan,
|
||||
@@ -427,7 +433,7 @@ func (c *Client) Intercept(interceptors ...Interceptor) {
|
||||
c.AuthIdentity, c.AuthIdentityChannel, c.BatchImageEvent, c.BatchImageItem,
|
||||
c.BatchImageJob, c.ChannelMonitor, c.ChannelMonitorDailyRollup,
|
||||
c.ChannelMonitorHistory, c.ChannelMonitorRequestTemplate,
|
||||
c.ErrorPassthroughRule, c.Group, c.IdempotencyRecord,
|
||||
c.CompositeModelRoute, c.ErrorPassthroughRule, c.Group, c.IdempotencyRecord,
|
||||
c.IdentityAdoptionDecision, c.PaymentAuditLog, c.PaymentOrder,
|
||||
c.PaymentProviderInstance, c.PendingAuthSession, c.PromoCode, c.PromoCodeUsage,
|
||||
c.Proxy, c.RedeemCode, c.SecuritySecret, c.Setting, c.SubscriptionPlan,
|
||||
@@ -470,6 +476,8 @@ func (c *Client) Mutate(ctx context.Context, m Mutation) (Value, error) {
|
||||
return c.ChannelMonitorHistory.mutate(ctx, m)
|
||||
case *ChannelMonitorRequestTemplateMutation:
|
||||
return c.ChannelMonitorRequestTemplate.mutate(ctx, m)
|
||||
case *CompositeModelRouteMutation:
|
||||
return c.CompositeModelRoute.mutate(ctx, m)
|
||||
case *ErrorPassthroughRuleMutation:
|
||||
return c.ErrorPassthroughRule.mutate(ctx, m)
|
||||
case *GroupMutation:
|
||||
@@ -2724,6 +2732,157 @@ func (c *ChannelMonitorRequestTemplateClient) mutate(ctx context.Context, m *Cha
|
||||
}
|
||||
}
|
||||
|
||||
// CompositeModelRouteClient is a client for the CompositeModelRoute schema.
|
||||
type CompositeModelRouteClient struct {
|
||||
config
|
||||
}
|
||||
|
||||
// NewCompositeModelRouteClient returns a client for the CompositeModelRoute from the given config.
|
||||
func NewCompositeModelRouteClient(c config) *CompositeModelRouteClient {
|
||||
return &CompositeModelRouteClient{config: c}
|
||||
}
|
||||
|
||||
// Use adds a list of mutation hooks to the hooks stack.
|
||||
// A call to `Use(f, g, h)` equals to `compositemodelroute.Hooks(f(g(h())))`.
|
||||
func (c *CompositeModelRouteClient) Use(hooks ...Hook) {
|
||||
c.hooks.CompositeModelRoute = append(c.hooks.CompositeModelRoute, hooks...)
|
||||
}
|
||||
|
||||
// Intercept adds a list of query interceptors to the interceptors stack.
|
||||
// A call to `Intercept(f, g, h)` equals to `compositemodelroute.Intercept(f(g(h())))`.
|
||||
func (c *CompositeModelRouteClient) Intercept(interceptors ...Interceptor) {
|
||||
c.inters.CompositeModelRoute = append(c.inters.CompositeModelRoute, interceptors...)
|
||||
}
|
||||
|
||||
// Create returns a builder for creating a CompositeModelRoute entity.
|
||||
func (c *CompositeModelRouteClient) Create() *CompositeModelRouteCreate {
|
||||
mutation := newCompositeModelRouteMutation(c.config, OpCreate)
|
||||
return &CompositeModelRouteCreate{config: c.config, hooks: c.Hooks(), mutation: mutation}
|
||||
}
|
||||
|
||||
// CreateBulk returns a builder for creating a bulk of CompositeModelRoute entities.
|
||||
func (c *CompositeModelRouteClient) CreateBulk(builders ...*CompositeModelRouteCreate) *CompositeModelRouteCreateBulk {
|
||||
return &CompositeModelRouteCreateBulk{config: c.config, builders: builders}
|
||||
}
|
||||
|
||||
// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates
|
||||
// a builder and applies setFunc on it.
|
||||
func (c *CompositeModelRouteClient) MapCreateBulk(slice any, setFunc func(*CompositeModelRouteCreate, int)) *CompositeModelRouteCreateBulk {
|
||||
rv := reflect.ValueOf(slice)
|
||||
if rv.Kind() != reflect.Slice {
|
||||
return &CompositeModelRouteCreateBulk{err: fmt.Errorf("calling to CompositeModelRouteClient.MapCreateBulk with wrong type %T, need slice", slice)}
|
||||
}
|
||||
builders := make([]*CompositeModelRouteCreate, rv.Len())
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
builders[i] = c.Create()
|
||||
setFunc(builders[i], i)
|
||||
}
|
||||
return &CompositeModelRouteCreateBulk{config: c.config, builders: builders}
|
||||
}
|
||||
|
||||
// Update returns an update builder for CompositeModelRoute.
|
||||
func (c *CompositeModelRouteClient) Update() *CompositeModelRouteUpdate {
|
||||
mutation := newCompositeModelRouteMutation(c.config, OpUpdate)
|
||||
return &CompositeModelRouteUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation}
|
||||
}
|
||||
|
||||
// UpdateOne returns an update builder for the given entity.
|
||||
func (c *CompositeModelRouteClient) UpdateOne(_m *CompositeModelRoute) *CompositeModelRouteUpdateOne {
|
||||
mutation := newCompositeModelRouteMutation(c.config, OpUpdateOne, withCompositeModelRoute(_m))
|
||||
return &CompositeModelRouteUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
|
||||
}
|
||||
|
||||
// UpdateOneID returns an update builder for the given id.
|
||||
func (c *CompositeModelRouteClient) UpdateOneID(id int64) *CompositeModelRouteUpdateOne {
|
||||
mutation := newCompositeModelRouteMutation(c.config, OpUpdateOne, withCompositeModelRouteID(id))
|
||||
return &CompositeModelRouteUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
|
||||
}
|
||||
|
||||
// Delete returns a delete builder for CompositeModelRoute.
|
||||
func (c *CompositeModelRouteClient) Delete() *CompositeModelRouteDelete {
|
||||
mutation := newCompositeModelRouteMutation(c.config, OpDelete)
|
||||
return &CompositeModelRouteDelete{config: c.config, hooks: c.Hooks(), mutation: mutation}
|
||||
}
|
||||
|
||||
// DeleteOne returns a builder for deleting the given entity.
|
||||
func (c *CompositeModelRouteClient) DeleteOne(_m *CompositeModelRoute) *CompositeModelRouteDeleteOne {
|
||||
return c.DeleteOneID(_m.ID)
|
||||
}
|
||||
|
||||
// DeleteOneID returns a builder for deleting the given entity by its id.
|
||||
func (c *CompositeModelRouteClient) DeleteOneID(id int64) *CompositeModelRouteDeleteOne {
|
||||
builder := c.Delete().Where(compositemodelroute.ID(id))
|
||||
builder.mutation.id = &id
|
||||
builder.mutation.op = OpDeleteOne
|
||||
return &CompositeModelRouteDeleteOne{builder}
|
||||
}
|
||||
|
||||
// Query returns a query builder for CompositeModelRoute.
|
||||
func (c *CompositeModelRouteClient) Query() *CompositeModelRouteQuery {
|
||||
return &CompositeModelRouteQuery{
|
||||
config: c.config,
|
||||
ctx: &QueryContext{Type: TypeCompositeModelRoute},
|
||||
inters: c.Interceptors(),
|
||||
}
|
||||
}
|
||||
|
||||
// Get returns a CompositeModelRoute entity by its id.
|
||||
func (c *CompositeModelRouteClient) Get(ctx context.Context, id int64) (*CompositeModelRoute, error) {
|
||||
return c.Query().Where(compositemodelroute.ID(id)).Only(ctx)
|
||||
}
|
||||
|
||||
// GetX is like Get, but panics if an error occurs.
|
||||
func (c *CompositeModelRouteClient) GetX(ctx context.Context, id int64) *CompositeModelRoute {
|
||||
obj, err := c.Get(ctx, id)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return obj
|
||||
}
|
||||
|
||||
// QueryGroup queries the group edge of a CompositeModelRoute.
|
||||
func (c *CompositeModelRouteClient) QueryGroup(_m *CompositeModelRoute) *GroupQuery {
|
||||
query := (&GroupClient{config: c.config}).Query()
|
||||
query.path = func(context.Context) (fromV *sql.Selector, _ error) {
|
||||
id := _m.ID
|
||||
step := sqlgraph.NewStep(
|
||||
sqlgraph.From(compositemodelroute.Table, compositemodelroute.FieldID, id),
|
||||
sqlgraph.To(group.Table, group.FieldID),
|
||||
sqlgraph.Edge(sqlgraph.M2O, false, compositemodelroute.GroupTable, compositemodelroute.GroupColumn),
|
||||
)
|
||||
fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step)
|
||||
return fromV, nil
|
||||
}
|
||||
return query
|
||||
}
|
||||
|
||||
// Hooks returns the client hooks.
|
||||
func (c *CompositeModelRouteClient) Hooks() []Hook {
|
||||
hooks := c.hooks.CompositeModelRoute
|
||||
return append(hooks[:len(hooks):len(hooks)], compositemodelroute.Hooks[:]...)
|
||||
}
|
||||
|
||||
// Interceptors returns the client interceptors.
|
||||
func (c *CompositeModelRouteClient) Interceptors() []Interceptor {
|
||||
inters := c.inters.CompositeModelRoute
|
||||
return append(inters[:len(inters):len(inters)], compositemodelroute.Interceptors[:]...)
|
||||
}
|
||||
|
||||
func (c *CompositeModelRouteClient) mutate(ctx context.Context, m *CompositeModelRouteMutation) (Value, error) {
|
||||
switch m.Op() {
|
||||
case OpCreate:
|
||||
return (&CompositeModelRouteCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
|
||||
case OpUpdate:
|
||||
return (&CompositeModelRouteUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
|
||||
case OpUpdateOne:
|
||||
return (&CompositeModelRouteUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
|
||||
case OpDelete, OpDeleteOne:
|
||||
return (&CompositeModelRouteDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx)
|
||||
default:
|
||||
return nil, fmt.Errorf("ent: unknown CompositeModelRoute mutation op: %q", m.Op())
|
||||
}
|
||||
}
|
||||
|
||||
// ErrorPassthroughRuleClient is a client for the ErrorPassthroughRule schema.
|
||||
type ErrorPassthroughRuleClient struct {
|
||||
config
|
||||
@@ -6669,23 +6828,25 @@ type (
|
||||
APIKey, Account, AccountGroup, Announcement, AnnouncementRead, AuthIdentity,
|
||||
AuthIdentityChannel, BatchImageEvent, BatchImageItem, BatchImageJob,
|
||||
ChannelMonitor, ChannelMonitorDailyRollup, ChannelMonitorHistory,
|
||||
ChannelMonitorRequestTemplate, ErrorPassthroughRule, Group, IdempotencyRecord,
|
||||
IdentityAdoptionDecision, PaymentAuditLog, PaymentOrder,
|
||||
PaymentProviderInstance, PendingAuthSession, PromoCode, PromoCodeUsage, Proxy,
|
||||
RedeemCode, SecuritySecret, Setting, SubscriptionPlan, TLSFingerprintProfile,
|
||||
UsageCleanupTask, UsageLog, User, UserAllowedGroup, UserAttributeDefinition,
|
||||
UserAttributeValue, UserPlatformQuota, UserSubscription []ent.Hook
|
||||
ChannelMonitorRequestTemplate, CompositeModelRoute,
|
||||
ErrorPassthroughRule, Group, IdempotencyRecord, IdentityAdoptionDecision,
|
||||
PaymentAuditLog, PaymentOrder, PaymentProviderInstance, PendingAuthSession,
|
||||
PromoCode, PromoCodeUsage, Proxy, RedeemCode, SecuritySecret, Setting,
|
||||
SubscriptionPlan, TLSFingerprintProfile, UsageCleanupTask, UsageLog, User,
|
||||
UserAllowedGroup, UserAttributeDefinition, UserAttributeValue,
|
||||
UserPlatformQuota, UserSubscription []ent.Hook
|
||||
}
|
||||
inters struct {
|
||||
APIKey, Account, AccountGroup, Announcement, AnnouncementRead, AuthIdentity,
|
||||
AuthIdentityChannel, BatchImageEvent, BatchImageItem, BatchImageJob,
|
||||
ChannelMonitor, ChannelMonitorDailyRollup, ChannelMonitorHistory,
|
||||
ChannelMonitorRequestTemplate, ErrorPassthroughRule, Group, IdempotencyRecord,
|
||||
IdentityAdoptionDecision, PaymentAuditLog, PaymentOrder,
|
||||
PaymentProviderInstance, PendingAuthSession, PromoCode, PromoCodeUsage, Proxy,
|
||||
RedeemCode, SecuritySecret, Setting, SubscriptionPlan, TLSFingerprintProfile,
|
||||
UsageCleanupTask, UsageLog, User, UserAllowedGroup, UserAttributeDefinition,
|
||||
UserAttributeValue, UserPlatformQuota, UserSubscription []ent.Interceptor
|
||||
ChannelMonitorRequestTemplate, CompositeModelRoute,
|
||||
ErrorPassthroughRule, Group, IdempotencyRecord, IdentityAdoptionDecision,
|
||||
PaymentAuditLog, PaymentOrder, PaymentProviderInstance, PendingAuthSession,
|
||||
PromoCode, PromoCodeUsage, Proxy, RedeemCode, SecuritySecret, Setting,
|
||||
SubscriptionPlan, TLSFingerprintProfile, UsageCleanupTask, UsageLog, User,
|
||||
UserAllowedGroup, UserAttributeDefinition, UserAttributeValue,
|
||||
UserPlatformQuota, UserSubscription []ent.Interceptor
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,264 @@
|
||||
// Code generated by ent, DO NOT EDIT.
|
||||
|
||||
package ent
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"entgo.io/ent"
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"github.com/Wei-Shaw/sub2api/ent/compositemodelroute"
|
||||
"github.com/Wei-Shaw/sub2api/ent/group"
|
||||
)
|
||||
|
||||
// CompositeModelRoute is the model entity for the CompositeModelRoute schema.
|
||||
type CompositeModelRoute struct {
|
||||
config `json:"-"`
|
||||
// ID of the ent.
|
||||
ID int64 `json:"id,omitempty"`
|
||||
// CreatedAt holds the value of the "created_at" field.
|
||||
CreatedAt time.Time `json:"created_at,omitempty"`
|
||||
// UpdatedAt holds the value of the "updated_at" field.
|
||||
UpdatedAt time.Time `json:"updated_at,omitempty"`
|
||||
// DeletedAt holds the value of the "deleted_at" field.
|
||||
DeletedAt *time.Time `json:"deleted_at,omitempty"`
|
||||
// GroupID holds the value of the "group_id" field.
|
||||
GroupID int64 `json:"group_id,omitempty"`
|
||||
// Client-facing model identifier or prefix.
|
||||
PublicModel string `json:"public_model,omitempty"`
|
||||
// exact or prefix.
|
||||
MatchType string `json:"match_type,omitempty"`
|
||||
// Concrete provider platform.
|
||||
TargetPlatform string `json:"target_platform,omitempty"`
|
||||
// Provider model identifier; empty means public_model.
|
||||
UpstreamModel string `json:"upstream_model,omitempty"`
|
||||
// Endpoint scope such as any, messages, responses, chat_completions.
|
||||
Endpoint string `json:"endpoint,omitempty"`
|
||||
// Lower values win within the same match strength.
|
||||
Priority int `json:"priority,omitempty"`
|
||||
// Enabled holds the value of the "enabled" field.
|
||||
Enabled bool `json:"enabled,omitempty"`
|
||||
// Notes holds the value of the "notes" field.
|
||||
Notes *string `json:"notes,omitempty"`
|
||||
// Edges holds the relations/edges for other nodes in the graph.
|
||||
// The values are being populated by the CompositeModelRouteQuery when eager-loading is set.
|
||||
Edges CompositeModelRouteEdges `json:"edges"`
|
||||
selectValues sql.SelectValues
|
||||
}
|
||||
|
||||
// CompositeModelRouteEdges holds the relations/edges for other nodes in the graph.
|
||||
type CompositeModelRouteEdges struct {
|
||||
// Group holds the value of the group edge.
|
||||
Group *Group `json:"group,omitempty"`
|
||||
// loadedTypes holds the information for reporting if a
|
||||
// type was loaded (or requested) in eager-loading or not.
|
||||
loadedTypes [1]bool
|
||||
}
|
||||
|
||||
// GroupOrErr returns the Group value or an error if the edge
|
||||
// was not loaded in eager-loading, or loaded but was not found.
|
||||
func (e CompositeModelRouteEdges) GroupOrErr() (*Group, error) {
|
||||
if e.Group != nil {
|
||||
return e.Group, nil
|
||||
} else if e.loadedTypes[0] {
|
||||
return nil, &NotFoundError{label: group.Label}
|
||||
}
|
||||
return nil, &NotLoadedError{edge: "group"}
|
||||
}
|
||||
|
||||
// scanValues returns the types for scanning values from sql.Rows.
|
||||
func (*CompositeModelRoute) scanValues(columns []string) ([]any, error) {
|
||||
values := make([]any, len(columns))
|
||||
for i := range columns {
|
||||
switch columns[i] {
|
||||
case compositemodelroute.FieldEnabled:
|
||||
values[i] = new(sql.NullBool)
|
||||
case compositemodelroute.FieldID, compositemodelroute.FieldGroupID, compositemodelroute.FieldPriority:
|
||||
values[i] = new(sql.NullInt64)
|
||||
case compositemodelroute.FieldPublicModel, compositemodelroute.FieldMatchType, compositemodelroute.FieldTargetPlatform, compositemodelroute.FieldUpstreamModel, compositemodelroute.FieldEndpoint, compositemodelroute.FieldNotes:
|
||||
values[i] = new(sql.NullString)
|
||||
case compositemodelroute.FieldCreatedAt, compositemodelroute.FieldUpdatedAt, compositemodelroute.FieldDeletedAt:
|
||||
values[i] = new(sql.NullTime)
|
||||
default:
|
||||
values[i] = new(sql.UnknownType)
|
||||
}
|
||||
}
|
||||
return values, nil
|
||||
}
|
||||
|
||||
// assignValues assigns the values that were returned from sql.Rows (after scanning)
|
||||
// to the CompositeModelRoute fields.
|
||||
func (_m *CompositeModelRoute) assignValues(columns []string, values []any) error {
|
||||
if m, n := len(values), len(columns); m < n {
|
||||
return fmt.Errorf("mismatch number of scan values: %d != %d", m, n)
|
||||
}
|
||||
for i := range columns {
|
||||
switch columns[i] {
|
||||
case compositemodelroute.FieldID:
|
||||
value, ok := values[i].(*sql.NullInt64)
|
||||
if !ok {
|
||||
return fmt.Errorf("unexpected type %T for field id", value)
|
||||
}
|
||||
_m.ID = int64(value.Int64)
|
||||
case compositemodelroute.FieldCreatedAt:
|
||||
if value, ok := values[i].(*sql.NullTime); !ok {
|
||||
return fmt.Errorf("unexpected type %T for field created_at", values[i])
|
||||
} else if value.Valid {
|
||||
_m.CreatedAt = value.Time
|
||||
}
|
||||
case compositemodelroute.FieldUpdatedAt:
|
||||
if value, ok := values[i].(*sql.NullTime); !ok {
|
||||
return fmt.Errorf("unexpected type %T for field updated_at", values[i])
|
||||
} else if value.Valid {
|
||||
_m.UpdatedAt = value.Time
|
||||
}
|
||||
case compositemodelroute.FieldDeletedAt:
|
||||
if value, ok := values[i].(*sql.NullTime); !ok {
|
||||
return fmt.Errorf("unexpected type %T for field deleted_at", values[i])
|
||||
} else if value.Valid {
|
||||
_m.DeletedAt = new(time.Time)
|
||||
*_m.DeletedAt = value.Time
|
||||
}
|
||||
case compositemodelroute.FieldGroupID:
|
||||
if value, ok := values[i].(*sql.NullInt64); !ok {
|
||||
return fmt.Errorf("unexpected type %T for field group_id", values[i])
|
||||
} else if value.Valid {
|
||||
_m.GroupID = value.Int64
|
||||
}
|
||||
case compositemodelroute.FieldPublicModel:
|
||||
if value, ok := values[i].(*sql.NullString); !ok {
|
||||
return fmt.Errorf("unexpected type %T for field public_model", values[i])
|
||||
} else if value.Valid {
|
||||
_m.PublicModel = value.String
|
||||
}
|
||||
case compositemodelroute.FieldMatchType:
|
||||
if value, ok := values[i].(*sql.NullString); !ok {
|
||||
return fmt.Errorf("unexpected type %T for field match_type", values[i])
|
||||
} else if value.Valid {
|
||||
_m.MatchType = value.String
|
||||
}
|
||||
case compositemodelroute.FieldTargetPlatform:
|
||||
if value, ok := values[i].(*sql.NullString); !ok {
|
||||
return fmt.Errorf("unexpected type %T for field target_platform", values[i])
|
||||
} else if value.Valid {
|
||||
_m.TargetPlatform = value.String
|
||||
}
|
||||
case compositemodelroute.FieldUpstreamModel:
|
||||
if value, ok := values[i].(*sql.NullString); !ok {
|
||||
return fmt.Errorf("unexpected type %T for field upstream_model", values[i])
|
||||
} else if value.Valid {
|
||||
_m.UpstreamModel = value.String
|
||||
}
|
||||
case compositemodelroute.FieldEndpoint:
|
||||
if value, ok := values[i].(*sql.NullString); !ok {
|
||||
return fmt.Errorf("unexpected type %T for field endpoint", values[i])
|
||||
} else if value.Valid {
|
||||
_m.Endpoint = value.String
|
||||
}
|
||||
case compositemodelroute.FieldPriority:
|
||||
if value, ok := values[i].(*sql.NullInt64); !ok {
|
||||
return fmt.Errorf("unexpected type %T for field priority", values[i])
|
||||
} else if value.Valid {
|
||||
_m.Priority = int(value.Int64)
|
||||
}
|
||||
case compositemodelroute.FieldEnabled:
|
||||
if value, ok := values[i].(*sql.NullBool); !ok {
|
||||
return fmt.Errorf("unexpected type %T for field enabled", values[i])
|
||||
} else if value.Valid {
|
||||
_m.Enabled = value.Bool
|
||||
}
|
||||
case compositemodelroute.FieldNotes:
|
||||
if value, ok := values[i].(*sql.NullString); !ok {
|
||||
return fmt.Errorf("unexpected type %T for field notes", values[i])
|
||||
} else if value.Valid {
|
||||
_m.Notes = new(string)
|
||||
*_m.Notes = value.String
|
||||
}
|
||||
default:
|
||||
_m.selectValues.Set(columns[i], values[i])
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Value returns the ent.Value that was dynamically selected and assigned to the CompositeModelRoute.
|
||||
// This includes values selected through modifiers, order, etc.
|
||||
func (_m *CompositeModelRoute) Value(name string) (ent.Value, error) {
|
||||
return _m.selectValues.Get(name)
|
||||
}
|
||||
|
||||
// QueryGroup queries the "group" edge of the CompositeModelRoute entity.
|
||||
func (_m *CompositeModelRoute) QueryGroup() *GroupQuery {
|
||||
return NewCompositeModelRouteClient(_m.config).QueryGroup(_m)
|
||||
}
|
||||
|
||||
// Update returns a builder for updating this CompositeModelRoute.
|
||||
// Note that you need to call CompositeModelRoute.Unwrap() before calling this method if this CompositeModelRoute
|
||||
// was returned from a transaction, and the transaction was committed or rolled back.
|
||||
func (_m *CompositeModelRoute) Update() *CompositeModelRouteUpdateOne {
|
||||
return NewCompositeModelRouteClient(_m.config).UpdateOne(_m)
|
||||
}
|
||||
|
||||
// Unwrap unwraps the CompositeModelRoute entity that was returned from a transaction after it was closed,
|
||||
// so that all future queries will be executed through the driver which created the transaction.
|
||||
func (_m *CompositeModelRoute) Unwrap() *CompositeModelRoute {
|
||||
_tx, ok := _m.config.driver.(*txDriver)
|
||||
if !ok {
|
||||
panic("ent: CompositeModelRoute is not a transactional entity")
|
||||
}
|
||||
_m.config.driver = _tx.drv
|
||||
return _m
|
||||
}
|
||||
|
||||
// String implements the fmt.Stringer.
|
||||
func (_m *CompositeModelRoute) String() string {
|
||||
var builder strings.Builder
|
||||
builder.WriteString("CompositeModelRoute(")
|
||||
builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID))
|
||||
builder.WriteString("created_at=")
|
||||
builder.WriteString(_m.CreatedAt.Format(time.ANSIC))
|
||||
builder.WriteString(", ")
|
||||
builder.WriteString("updated_at=")
|
||||
builder.WriteString(_m.UpdatedAt.Format(time.ANSIC))
|
||||
builder.WriteString(", ")
|
||||
if v := _m.DeletedAt; v != nil {
|
||||
builder.WriteString("deleted_at=")
|
||||
builder.WriteString(v.Format(time.ANSIC))
|
||||
}
|
||||
builder.WriteString(", ")
|
||||
builder.WriteString("group_id=")
|
||||
builder.WriteString(fmt.Sprintf("%v", _m.GroupID))
|
||||
builder.WriteString(", ")
|
||||
builder.WriteString("public_model=")
|
||||
builder.WriteString(_m.PublicModel)
|
||||
builder.WriteString(", ")
|
||||
builder.WriteString("match_type=")
|
||||
builder.WriteString(_m.MatchType)
|
||||
builder.WriteString(", ")
|
||||
builder.WriteString("target_platform=")
|
||||
builder.WriteString(_m.TargetPlatform)
|
||||
builder.WriteString(", ")
|
||||
builder.WriteString("upstream_model=")
|
||||
builder.WriteString(_m.UpstreamModel)
|
||||
builder.WriteString(", ")
|
||||
builder.WriteString("endpoint=")
|
||||
builder.WriteString(_m.Endpoint)
|
||||
builder.WriteString(", ")
|
||||
builder.WriteString("priority=")
|
||||
builder.WriteString(fmt.Sprintf("%v", _m.Priority))
|
||||
builder.WriteString(", ")
|
||||
builder.WriteString("enabled=")
|
||||
builder.WriteString(fmt.Sprintf("%v", _m.Enabled))
|
||||
builder.WriteString(", ")
|
||||
if v := _m.Notes; v != nil {
|
||||
builder.WriteString("notes=")
|
||||
builder.WriteString(*v)
|
||||
}
|
||||
builder.WriteByte(')')
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
// CompositeModelRoutes is a parsable slice of CompositeModelRoute.
|
||||
type CompositeModelRoutes []*CompositeModelRoute
|
||||
@@ -0,0 +1,200 @@
|
||||
// Code generated by ent, DO NOT EDIT.
|
||||
|
||||
package compositemodelroute
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"entgo.io/ent"
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
)
|
||||
|
||||
const (
|
||||
// Label holds the string label denoting the compositemodelroute type in the database.
|
||||
Label = "composite_model_route"
|
||||
// FieldID holds the string denoting the id field in the database.
|
||||
FieldID = "id"
|
||||
// FieldCreatedAt holds the string denoting the created_at field in the database.
|
||||
FieldCreatedAt = "created_at"
|
||||
// FieldUpdatedAt holds the string denoting the updated_at field in the database.
|
||||
FieldUpdatedAt = "updated_at"
|
||||
// FieldDeletedAt holds the string denoting the deleted_at field in the database.
|
||||
FieldDeletedAt = "deleted_at"
|
||||
// FieldGroupID holds the string denoting the group_id field in the database.
|
||||
FieldGroupID = "group_id"
|
||||
// FieldPublicModel holds the string denoting the public_model field in the database.
|
||||
FieldPublicModel = "public_model"
|
||||
// FieldMatchType holds the string denoting the match_type field in the database.
|
||||
FieldMatchType = "match_type"
|
||||
// FieldTargetPlatform holds the string denoting the target_platform field in the database.
|
||||
FieldTargetPlatform = "target_platform"
|
||||
// FieldUpstreamModel holds the string denoting the upstream_model field in the database.
|
||||
FieldUpstreamModel = "upstream_model"
|
||||
// FieldEndpoint holds the string denoting the endpoint field in the database.
|
||||
FieldEndpoint = "endpoint"
|
||||
// FieldPriority holds the string denoting the priority field in the database.
|
||||
FieldPriority = "priority"
|
||||
// FieldEnabled holds the string denoting the enabled field in the database.
|
||||
FieldEnabled = "enabled"
|
||||
// FieldNotes holds the string denoting the notes field in the database.
|
||||
FieldNotes = "notes"
|
||||
// EdgeGroup holds the string denoting the group edge name in mutations.
|
||||
EdgeGroup = "group"
|
||||
// Table holds the table name of the compositemodelroute in the database.
|
||||
Table = "composite_model_routes"
|
||||
// GroupTable is the table that holds the group relation/edge.
|
||||
GroupTable = "composite_model_routes"
|
||||
// GroupInverseTable is the table name for the Group entity.
|
||||
// It exists in this package in order to avoid circular dependency with the "group" package.
|
||||
GroupInverseTable = "groups"
|
||||
// GroupColumn is the table column denoting the group relation/edge.
|
||||
GroupColumn = "group_id"
|
||||
)
|
||||
|
||||
// Columns holds all SQL columns for compositemodelroute fields.
|
||||
var Columns = []string{
|
||||
FieldID,
|
||||
FieldCreatedAt,
|
||||
FieldUpdatedAt,
|
||||
FieldDeletedAt,
|
||||
FieldGroupID,
|
||||
FieldPublicModel,
|
||||
FieldMatchType,
|
||||
FieldTargetPlatform,
|
||||
FieldUpstreamModel,
|
||||
FieldEndpoint,
|
||||
FieldPriority,
|
||||
FieldEnabled,
|
||||
FieldNotes,
|
||||
}
|
||||
|
||||
// ValidColumn reports if the column name is valid (part of the table columns).
|
||||
func ValidColumn(column string) bool {
|
||||
for i := range Columns {
|
||||
if column == Columns[i] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Note that the variables below are initialized by the runtime
|
||||
// package on the initialization of the application. Therefore,
|
||||
// it should be imported in the main as follows:
|
||||
//
|
||||
// import _ "github.com/Wei-Shaw/sub2api/ent/runtime"
|
||||
var (
|
||||
Hooks [1]ent.Hook
|
||||
Interceptors [1]ent.Interceptor
|
||||
// DefaultCreatedAt holds the default value on creation for the "created_at" field.
|
||||
DefaultCreatedAt func() time.Time
|
||||
// DefaultUpdatedAt holds the default value on creation for the "updated_at" field.
|
||||
DefaultUpdatedAt func() time.Time
|
||||
// UpdateDefaultUpdatedAt holds the default value on update for the "updated_at" field.
|
||||
UpdateDefaultUpdatedAt func() time.Time
|
||||
// PublicModelValidator is a validator for the "public_model" field. It is called by the builders before save.
|
||||
PublicModelValidator func(string) error
|
||||
// DefaultMatchType holds the default value on creation for the "match_type" field.
|
||||
DefaultMatchType string
|
||||
// MatchTypeValidator is a validator for the "match_type" field. It is called by the builders before save.
|
||||
MatchTypeValidator func(string) error
|
||||
// DefaultTargetPlatform holds the default value on creation for the "target_platform" field.
|
||||
DefaultTargetPlatform string
|
||||
// TargetPlatformValidator is a validator for the "target_platform" field. It is called by the builders before save.
|
||||
TargetPlatformValidator func(string) error
|
||||
// DefaultUpstreamModel holds the default value on creation for the "upstream_model" field.
|
||||
DefaultUpstreamModel string
|
||||
// UpstreamModelValidator is a validator for the "upstream_model" field. It is called by the builders before save.
|
||||
UpstreamModelValidator func(string) error
|
||||
// DefaultEndpoint holds the default value on creation for the "endpoint" field.
|
||||
DefaultEndpoint string
|
||||
// EndpointValidator is a validator for the "endpoint" field. It is called by the builders before save.
|
||||
EndpointValidator func(string) error
|
||||
// DefaultPriority holds the default value on creation for the "priority" field.
|
||||
DefaultPriority int
|
||||
// DefaultEnabled holds the default value on creation for the "enabled" field.
|
||||
DefaultEnabled bool
|
||||
)
|
||||
|
||||
// OrderOption defines the ordering options for the CompositeModelRoute queries.
|
||||
type OrderOption func(*sql.Selector)
|
||||
|
||||
// ByID orders the results by the id field.
|
||||
func ByID(opts ...sql.OrderTermOption) OrderOption {
|
||||
return sql.OrderByField(FieldID, opts...).ToFunc()
|
||||
}
|
||||
|
||||
// ByCreatedAt orders the results by the created_at field.
|
||||
func ByCreatedAt(opts ...sql.OrderTermOption) OrderOption {
|
||||
return sql.OrderByField(FieldCreatedAt, opts...).ToFunc()
|
||||
}
|
||||
|
||||
// ByUpdatedAt orders the results by the updated_at field.
|
||||
func ByUpdatedAt(opts ...sql.OrderTermOption) OrderOption {
|
||||
return sql.OrderByField(FieldUpdatedAt, opts...).ToFunc()
|
||||
}
|
||||
|
||||
// ByDeletedAt orders the results by the deleted_at field.
|
||||
func ByDeletedAt(opts ...sql.OrderTermOption) OrderOption {
|
||||
return sql.OrderByField(FieldDeletedAt, opts...).ToFunc()
|
||||
}
|
||||
|
||||
// ByGroupID orders the results by the group_id field.
|
||||
func ByGroupID(opts ...sql.OrderTermOption) OrderOption {
|
||||
return sql.OrderByField(FieldGroupID, opts...).ToFunc()
|
||||
}
|
||||
|
||||
// ByPublicModel orders the results by the public_model field.
|
||||
func ByPublicModel(opts ...sql.OrderTermOption) OrderOption {
|
||||
return sql.OrderByField(FieldPublicModel, opts...).ToFunc()
|
||||
}
|
||||
|
||||
// ByMatchType orders the results by the match_type field.
|
||||
func ByMatchType(opts ...sql.OrderTermOption) OrderOption {
|
||||
return sql.OrderByField(FieldMatchType, opts...).ToFunc()
|
||||
}
|
||||
|
||||
// ByTargetPlatform orders the results by the target_platform field.
|
||||
func ByTargetPlatform(opts ...sql.OrderTermOption) OrderOption {
|
||||
return sql.OrderByField(FieldTargetPlatform, opts...).ToFunc()
|
||||
}
|
||||
|
||||
// ByUpstreamModel orders the results by the upstream_model field.
|
||||
func ByUpstreamModel(opts ...sql.OrderTermOption) OrderOption {
|
||||
return sql.OrderByField(FieldUpstreamModel, opts...).ToFunc()
|
||||
}
|
||||
|
||||
// ByEndpoint orders the results by the endpoint field.
|
||||
func ByEndpoint(opts ...sql.OrderTermOption) OrderOption {
|
||||
return sql.OrderByField(FieldEndpoint, opts...).ToFunc()
|
||||
}
|
||||
|
||||
// ByPriority orders the results by the priority field.
|
||||
func ByPriority(opts ...sql.OrderTermOption) OrderOption {
|
||||
return sql.OrderByField(FieldPriority, opts...).ToFunc()
|
||||
}
|
||||
|
||||
// ByEnabled orders the results by the enabled field.
|
||||
func ByEnabled(opts ...sql.OrderTermOption) OrderOption {
|
||||
return sql.OrderByField(FieldEnabled, opts...).ToFunc()
|
||||
}
|
||||
|
||||
// ByNotes orders the results by the notes field.
|
||||
func ByNotes(opts ...sql.OrderTermOption) OrderOption {
|
||||
return sql.OrderByField(FieldNotes, opts...).ToFunc()
|
||||
}
|
||||
|
||||
// ByGroupField orders the results by group field.
|
||||
func ByGroupField(field string, opts ...sql.OrderTermOption) OrderOption {
|
||||
return func(s *sql.Selector) {
|
||||
sqlgraph.OrderByNeighborTerms(s, newGroupStep(), sql.OrderByField(field, opts...))
|
||||
}
|
||||
}
|
||||
func newGroupStep() *sqlgraph.Step {
|
||||
return sqlgraph.NewStep(
|
||||
sqlgraph.From(Table, FieldID),
|
||||
sqlgraph.To(GroupInverseTable, FieldID),
|
||||
sqlgraph.Edge(sqlgraph.M2O, false, GroupTable, GroupColumn),
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,754 @@
|
||||
// Code generated by ent, DO NOT EDIT.
|
||||
|
||||
package compositemodelroute
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"github.com/Wei-Shaw/sub2api/ent/predicate"
|
||||
)
|
||||
|
||||
// ID filters vertices based on their ID field.
|
||||
func ID(id int64) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldEQ(FieldID, id))
|
||||
}
|
||||
|
||||
// IDEQ applies the EQ predicate on the ID field.
|
||||
func IDEQ(id int64) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldEQ(FieldID, id))
|
||||
}
|
||||
|
||||
// IDNEQ applies the NEQ predicate on the ID field.
|
||||
func IDNEQ(id int64) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldNEQ(FieldID, id))
|
||||
}
|
||||
|
||||
// IDIn applies the In predicate on the ID field.
|
||||
func IDIn(ids ...int64) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldIn(FieldID, ids...))
|
||||
}
|
||||
|
||||
// IDNotIn applies the NotIn predicate on the ID field.
|
||||
func IDNotIn(ids ...int64) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldNotIn(FieldID, ids...))
|
||||
}
|
||||
|
||||
// IDGT applies the GT predicate on the ID field.
|
||||
func IDGT(id int64) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldGT(FieldID, id))
|
||||
}
|
||||
|
||||
// IDGTE applies the GTE predicate on the ID field.
|
||||
func IDGTE(id int64) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldGTE(FieldID, id))
|
||||
}
|
||||
|
||||
// IDLT applies the LT predicate on the ID field.
|
||||
func IDLT(id int64) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldLT(FieldID, id))
|
||||
}
|
||||
|
||||
// IDLTE applies the LTE predicate on the ID field.
|
||||
func IDLTE(id int64) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldLTE(FieldID, id))
|
||||
}
|
||||
|
||||
// CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ.
|
||||
func CreatedAt(v time.Time) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldEQ(FieldCreatedAt, v))
|
||||
}
|
||||
|
||||
// UpdatedAt applies equality check predicate on the "updated_at" field. It's identical to UpdatedAtEQ.
|
||||
func UpdatedAt(v time.Time) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldEQ(FieldUpdatedAt, v))
|
||||
}
|
||||
|
||||
// DeletedAt applies equality check predicate on the "deleted_at" field. It's identical to DeletedAtEQ.
|
||||
func DeletedAt(v time.Time) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldEQ(FieldDeletedAt, v))
|
||||
}
|
||||
|
||||
// GroupID applies equality check predicate on the "group_id" field. It's identical to GroupIDEQ.
|
||||
func GroupID(v int64) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldEQ(FieldGroupID, v))
|
||||
}
|
||||
|
||||
// PublicModel applies equality check predicate on the "public_model" field. It's identical to PublicModelEQ.
|
||||
func PublicModel(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldEQ(FieldPublicModel, v))
|
||||
}
|
||||
|
||||
// MatchType applies equality check predicate on the "match_type" field. It's identical to MatchTypeEQ.
|
||||
func MatchType(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldEQ(FieldMatchType, v))
|
||||
}
|
||||
|
||||
// TargetPlatform applies equality check predicate on the "target_platform" field. It's identical to TargetPlatformEQ.
|
||||
func TargetPlatform(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldEQ(FieldTargetPlatform, v))
|
||||
}
|
||||
|
||||
// UpstreamModel applies equality check predicate on the "upstream_model" field. It's identical to UpstreamModelEQ.
|
||||
func UpstreamModel(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldEQ(FieldUpstreamModel, v))
|
||||
}
|
||||
|
||||
// Endpoint applies equality check predicate on the "endpoint" field. It's identical to EndpointEQ.
|
||||
func Endpoint(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldEQ(FieldEndpoint, v))
|
||||
}
|
||||
|
||||
// Priority applies equality check predicate on the "priority" field. It's identical to PriorityEQ.
|
||||
func Priority(v int) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldEQ(FieldPriority, v))
|
||||
}
|
||||
|
||||
// Enabled applies equality check predicate on the "enabled" field. It's identical to EnabledEQ.
|
||||
func Enabled(v bool) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldEQ(FieldEnabled, v))
|
||||
}
|
||||
|
||||
// Notes applies equality check predicate on the "notes" field. It's identical to NotesEQ.
|
||||
func Notes(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldEQ(FieldNotes, v))
|
||||
}
|
||||
|
||||
// CreatedAtEQ applies the EQ predicate on the "created_at" field.
|
||||
func CreatedAtEQ(v time.Time) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldEQ(FieldCreatedAt, v))
|
||||
}
|
||||
|
||||
// CreatedAtNEQ applies the NEQ predicate on the "created_at" field.
|
||||
func CreatedAtNEQ(v time.Time) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldNEQ(FieldCreatedAt, v))
|
||||
}
|
||||
|
||||
// CreatedAtIn applies the In predicate on the "created_at" field.
|
||||
func CreatedAtIn(vs ...time.Time) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldIn(FieldCreatedAt, vs...))
|
||||
}
|
||||
|
||||
// CreatedAtNotIn applies the NotIn predicate on the "created_at" field.
|
||||
func CreatedAtNotIn(vs ...time.Time) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldNotIn(FieldCreatedAt, vs...))
|
||||
}
|
||||
|
||||
// CreatedAtGT applies the GT predicate on the "created_at" field.
|
||||
func CreatedAtGT(v time.Time) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldGT(FieldCreatedAt, v))
|
||||
}
|
||||
|
||||
// CreatedAtGTE applies the GTE predicate on the "created_at" field.
|
||||
func CreatedAtGTE(v time.Time) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldGTE(FieldCreatedAt, v))
|
||||
}
|
||||
|
||||
// CreatedAtLT applies the LT predicate on the "created_at" field.
|
||||
func CreatedAtLT(v time.Time) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldLT(FieldCreatedAt, v))
|
||||
}
|
||||
|
||||
// CreatedAtLTE applies the LTE predicate on the "created_at" field.
|
||||
func CreatedAtLTE(v time.Time) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldLTE(FieldCreatedAt, v))
|
||||
}
|
||||
|
||||
// UpdatedAtEQ applies the EQ predicate on the "updated_at" field.
|
||||
func UpdatedAtEQ(v time.Time) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldEQ(FieldUpdatedAt, v))
|
||||
}
|
||||
|
||||
// UpdatedAtNEQ applies the NEQ predicate on the "updated_at" field.
|
||||
func UpdatedAtNEQ(v time.Time) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldNEQ(FieldUpdatedAt, v))
|
||||
}
|
||||
|
||||
// UpdatedAtIn applies the In predicate on the "updated_at" field.
|
||||
func UpdatedAtIn(vs ...time.Time) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldIn(FieldUpdatedAt, vs...))
|
||||
}
|
||||
|
||||
// UpdatedAtNotIn applies the NotIn predicate on the "updated_at" field.
|
||||
func UpdatedAtNotIn(vs ...time.Time) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldNotIn(FieldUpdatedAt, vs...))
|
||||
}
|
||||
|
||||
// UpdatedAtGT applies the GT predicate on the "updated_at" field.
|
||||
func UpdatedAtGT(v time.Time) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldGT(FieldUpdatedAt, v))
|
||||
}
|
||||
|
||||
// UpdatedAtGTE applies the GTE predicate on the "updated_at" field.
|
||||
func UpdatedAtGTE(v time.Time) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldGTE(FieldUpdatedAt, v))
|
||||
}
|
||||
|
||||
// UpdatedAtLT applies the LT predicate on the "updated_at" field.
|
||||
func UpdatedAtLT(v time.Time) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldLT(FieldUpdatedAt, v))
|
||||
}
|
||||
|
||||
// UpdatedAtLTE applies the LTE predicate on the "updated_at" field.
|
||||
func UpdatedAtLTE(v time.Time) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldLTE(FieldUpdatedAt, v))
|
||||
}
|
||||
|
||||
// DeletedAtEQ applies the EQ predicate on the "deleted_at" field.
|
||||
func DeletedAtEQ(v time.Time) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldEQ(FieldDeletedAt, v))
|
||||
}
|
||||
|
||||
// DeletedAtNEQ applies the NEQ predicate on the "deleted_at" field.
|
||||
func DeletedAtNEQ(v time.Time) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldNEQ(FieldDeletedAt, v))
|
||||
}
|
||||
|
||||
// DeletedAtIn applies the In predicate on the "deleted_at" field.
|
||||
func DeletedAtIn(vs ...time.Time) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldIn(FieldDeletedAt, vs...))
|
||||
}
|
||||
|
||||
// DeletedAtNotIn applies the NotIn predicate on the "deleted_at" field.
|
||||
func DeletedAtNotIn(vs ...time.Time) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldNotIn(FieldDeletedAt, vs...))
|
||||
}
|
||||
|
||||
// DeletedAtGT applies the GT predicate on the "deleted_at" field.
|
||||
func DeletedAtGT(v time.Time) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldGT(FieldDeletedAt, v))
|
||||
}
|
||||
|
||||
// DeletedAtGTE applies the GTE predicate on the "deleted_at" field.
|
||||
func DeletedAtGTE(v time.Time) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldGTE(FieldDeletedAt, v))
|
||||
}
|
||||
|
||||
// DeletedAtLT applies the LT predicate on the "deleted_at" field.
|
||||
func DeletedAtLT(v time.Time) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldLT(FieldDeletedAt, v))
|
||||
}
|
||||
|
||||
// DeletedAtLTE applies the LTE predicate on the "deleted_at" field.
|
||||
func DeletedAtLTE(v time.Time) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldLTE(FieldDeletedAt, v))
|
||||
}
|
||||
|
||||
// DeletedAtIsNil applies the IsNil predicate on the "deleted_at" field.
|
||||
func DeletedAtIsNil() predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldIsNull(FieldDeletedAt))
|
||||
}
|
||||
|
||||
// DeletedAtNotNil applies the NotNil predicate on the "deleted_at" field.
|
||||
func DeletedAtNotNil() predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldNotNull(FieldDeletedAt))
|
||||
}
|
||||
|
||||
// GroupIDEQ applies the EQ predicate on the "group_id" field.
|
||||
func GroupIDEQ(v int64) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldEQ(FieldGroupID, v))
|
||||
}
|
||||
|
||||
// GroupIDNEQ applies the NEQ predicate on the "group_id" field.
|
||||
func GroupIDNEQ(v int64) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldNEQ(FieldGroupID, v))
|
||||
}
|
||||
|
||||
// GroupIDIn applies the In predicate on the "group_id" field.
|
||||
func GroupIDIn(vs ...int64) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldIn(FieldGroupID, vs...))
|
||||
}
|
||||
|
||||
// GroupIDNotIn applies the NotIn predicate on the "group_id" field.
|
||||
func GroupIDNotIn(vs ...int64) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldNotIn(FieldGroupID, vs...))
|
||||
}
|
||||
|
||||
// PublicModelEQ applies the EQ predicate on the "public_model" field.
|
||||
func PublicModelEQ(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldEQ(FieldPublicModel, v))
|
||||
}
|
||||
|
||||
// PublicModelNEQ applies the NEQ predicate on the "public_model" field.
|
||||
func PublicModelNEQ(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldNEQ(FieldPublicModel, v))
|
||||
}
|
||||
|
||||
// PublicModelIn applies the In predicate on the "public_model" field.
|
||||
func PublicModelIn(vs ...string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldIn(FieldPublicModel, vs...))
|
||||
}
|
||||
|
||||
// PublicModelNotIn applies the NotIn predicate on the "public_model" field.
|
||||
func PublicModelNotIn(vs ...string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldNotIn(FieldPublicModel, vs...))
|
||||
}
|
||||
|
||||
// PublicModelGT applies the GT predicate on the "public_model" field.
|
||||
func PublicModelGT(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldGT(FieldPublicModel, v))
|
||||
}
|
||||
|
||||
// PublicModelGTE applies the GTE predicate on the "public_model" field.
|
||||
func PublicModelGTE(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldGTE(FieldPublicModel, v))
|
||||
}
|
||||
|
||||
// PublicModelLT applies the LT predicate on the "public_model" field.
|
||||
func PublicModelLT(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldLT(FieldPublicModel, v))
|
||||
}
|
||||
|
||||
// PublicModelLTE applies the LTE predicate on the "public_model" field.
|
||||
func PublicModelLTE(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldLTE(FieldPublicModel, v))
|
||||
}
|
||||
|
||||
// PublicModelContains applies the Contains predicate on the "public_model" field.
|
||||
func PublicModelContains(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldContains(FieldPublicModel, v))
|
||||
}
|
||||
|
||||
// PublicModelHasPrefix applies the HasPrefix predicate on the "public_model" field.
|
||||
func PublicModelHasPrefix(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldHasPrefix(FieldPublicModel, v))
|
||||
}
|
||||
|
||||
// PublicModelHasSuffix applies the HasSuffix predicate on the "public_model" field.
|
||||
func PublicModelHasSuffix(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldHasSuffix(FieldPublicModel, v))
|
||||
}
|
||||
|
||||
// PublicModelEqualFold applies the EqualFold predicate on the "public_model" field.
|
||||
func PublicModelEqualFold(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldEqualFold(FieldPublicModel, v))
|
||||
}
|
||||
|
||||
// PublicModelContainsFold applies the ContainsFold predicate on the "public_model" field.
|
||||
func PublicModelContainsFold(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldContainsFold(FieldPublicModel, v))
|
||||
}
|
||||
|
||||
// MatchTypeEQ applies the EQ predicate on the "match_type" field.
|
||||
func MatchTypeEQ(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldEQ(FieldMatchType, v))
|
||||
}
|
||||
|
||||
// MatchTypeNEQ applies the NEQ predicate on the "match_type" field.
|
||||
func MatchTypeNEQ(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldNEQ(FieldMatchType, v))
|
||||
}
|
||||
|
||||
// MatchTypeIn applies the In predicate on the "match_type" field.
|
||||
func MatchTypeIn(vs ...string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldIn(FieldMatchType, vs...))
|
||||
}
|
||||
|
||||
// MatchTypeNotIn applies the NotIn predicate on the "match_type" field.
|
||||
func MatchTypeNotIn(vs ...string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldNotIn(FieldMatchType, vs...))
|
||||
}
|
||||
|
||||
// MatchTypeGT applies the GT predicate on the "match_type" field.
|
||||
func MatchTypeGT(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldGT(FieldMatchType, v))
|
||||
}
|
||||
|
||||
// MatchTypeGTE applies the GTE predicate on the "match_type" field.
|
||||
func MatchTypeGTE(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldGTE(FieldMatchType, v))
|
||||
}
|
||||
|
||||
// MatchTypeLT applies the LT predicate on the "match_type" field.
|
||||
func MatchTypeLT(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldLT(FieldMatchType, v))
|
||||
}
|
||||
|
||||
// MatchTypeLTE applies the LTE predicate on the "match_type" field.
|
||||
func MatchTypeLTE(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldLTE(FieldMatchType, v))
|
||||
}
|
||||
|
||||
// MatchTypeContains applies the Contains predicate on the "match_type" field.
|
||||
func MatchTypeContains(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldContains(FieldMatchType, v))
|
||||
}
|
||||
|
||||
// MatchTypeHasPrefix applies the HasPrefix predicate on the "match_type" field.
|
||||
func MatchTypeHasPrefix(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldHasPrefix(FieldMatchType, v))
|
||||
}
|
||||
|
||||
// MatchTypeHasSuffix applies the HasSuffix predicate on the "match_type" field.
|
||||
func MatchTypeHasSuffix(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldHasSuffix(FieldMatchType, v))
|
||||
}
|
||||
|
||||
// MatchTypeEqualFold applies the EqualFold predicate on the "match_type" field.
|
||||
func MatchTypeEqualFold(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldEqualFold(FieldMatchType, v))
|
||||
}
|
||||
|
||||
// MatchTypeContainsFold applies the ContainsFold predicate on the "match_type" field.
|
||||
func MatchTypeContainsFold(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldContainsFold(FieldMatchType, v))
|
||||
}
|
||||
|
||||
// TargetPlatformEQ applies the EQ predicate on the "target_platform" field.
|
||||
func TargetPlatformEQ(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldEQ(FieldTargetPlatform, v))
|
||||
}
|
||||
|
||||
// TargetPlatformNEQ applies the NEQ predicate on the "target_platform" field.
|
||||
func TargetPlatformNEQ(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldNEQ(FieldTargetPlatform, v))
|
||||
}
|
||||
|
||||
// TargetPlatformIn applies the In predicate on the "target_platform" field.
|
||||
func TargetPlatformIn(vs ...string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldIn(FieldTargetPlatform, vs...))
|
||||
}
|
||||
|
||||
// TargetPlatformNotIn applies the NotIn predicate on the "target_platform" field.
|
||||
func TargetPlatformNotIn(vs ...string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldNotIn(FieldTargetPlatform, vs...))
|
||||
}
|
||||
|
||||
// TargetPlatformGT applies the GT predicate on the "target_platform" field.
|
||||
func TargetPlatformGT(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldGT(FieldTargetPlatform, v))
|
||||
}
|
||||
|
||||
// TargetPlatformGTE applies the GTE predicate on the "target_platform" field.
|
||||
func TargetPlatformGTE(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldGTE(FieldTargetPlatform, v))
|
||||
}
|
||||
|
||||
// TargetPlatformLT applies the LT predicate on the "target_platform" field.
|
||||
func TargetPlatformLT(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldLT(FieldTargetPlatform, v))
|
||||
}
|
||||
|
||||
// TargetPlatformLTE applies the LTE predicate on the "target_platform" field.
|
||||
func TargetPlatformLTE(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldLTE(FieldTargetPlatform, v))
|
||||
}
|
||||
|
||||
// TargetPlatformContains applies the Contains predicate on the "target_platform" field.
|
||||
func TargetPlatformContains(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldContains(FieldTargetPlatform, v))
|
||||
}
|
||||
|
||||
// TargetPlatformHasPrefix applies the HasPrefix predicate on the "target_platform" field.
|
||||
func TargetPlatformHasPrefix(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldHasPrefix(FieldTargetPlatform, v))
|
||||
}
|
||||
|
||||
// TargetPlatformHasSuffix applies the HasSuffix predicate on the "target_platform" field.
|
||||
func TargetPlatformHasSuffix(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldHasSuffix(FieldTargetPlatform, v))
|
||||
}
|
||||
|
||||
// TargetPlatformEqualFold applies the EqualFold predicate on the "target_platform" field.
|
||||
func TargetPlatformEqualFold(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldEqualFold(FieldTargetPlatform, v))
|
||||
}
|
||||
|
||||
// TargetPlatformContainsFold applies the ContainsFold predicate on the "target_platform" field.
|
||||
func TargetPlatformContainsFold(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldContainsFold(FieldTargetPlatform, v))
|
||||
}
|
||||
|
||||
// UpstreamModelEQ applies the EQ predicate on the "upstream_model" field.
|
||||
func UpstreamModelEQ(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldEQ(FieldUpstreamModel, v))
|
||||
}
|
||||
|
||||
// UpstreamModelNEQ applies the NEQ predicate on the "upstream_model" field.
|
||||
func UpstreamModelNEQ(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldNEQ(FieldUpstreamModel, v))
|
||||
}
|
||||
|
||||
// UpstreamModelIn applies the In predicate on the "upstream_model" field.
|
||||
func UpstreamModelIn(vs ...string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldIn(FieldUpstreamModel, vs...))
|
||||
}
|
||||
|
||||
// UpstreamModelNotIn applies the NotIn predicate on the "upstream_model" field.
|
||||
func UpstreamModelNotIn(vs ...string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldNotIn(FieldUpstreamModel, vs...))
|
||||
}
|
||||
|
||||
// UpstreamModelGT applies the GT predicate on the "upstream_model" field.
|
||||
func UpstreamModelGT(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldGT(FieldUpstreamModel, v))
|
||||
}
|
||||
|
||||
// UpstreamModelGTE applies the GTE predicate on the "upstream_model" field.
|
||||
func UpstreamModelGTE(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldGTE(FieldUpstreamModel, v))
|
||||
}
|
||||
|
||||
// UpstreamModelLT applies the LT predicate on the "upstream_model" field.
|
||||
func UpstreamModelLT(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldLT(FieldUpstreamModel, v))
|
||||
}
|
||||
|
||||
// UpstreamModelLTE applies the LTE predicate on the "upstream_model" field.
|
||||
func UpstreamModelLTE(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldLTE(FieldUpstreamModel, v))
|
||||
}
|
||||
|
||||
// UpstreamModelContains applies the Contains predicate on the "upstream_model" field.
|
||||
func UpstreamModelContains(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldContains(FieldUpstreamModel, v))
|
||||
}
|
||||
|
||||
// UpstreamModelHasPrefix applies the HasPrefix predicate on the "upstream_model" field.
|
||||
func UpstreamModelHasPrefix(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldHasPrefix(FieldUpstreamModel, v))
|
||||
}
|
||||
|
||||
// UpstreamModelHasSuffix applies the HasSuffix predicate on the "upstream_model" field.
|
||||
func UpstreamModelHasSuffix(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldHasSuffix(FieldUpstreamModel, v))
|
||||
}
|
||||
|
||||
// UpstreamModelEqualFold applies the EqualFold predicate on the "upstream_model" field.
|
||||
func UpstreamModelEqualFold(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldEqualFold(FieldUpstreamModel, v))
|
||||
}
|
||||
|
||||
// UpstreamModelContainsFold applies the ContainsFold predicate on the "upstream_model" field.
|
||||
func UpstreamModelContainsFold(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldContainsFold(FieldUpstreamModel, v))
|
||||
}
|
||||
|
||||
// EndpointEQ applies the EQ predicate on the "endpoint" field.
|
||||
func EndpointEQ(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldEQ(FieldEndpoint, v))
|
||||
}
|
||||
|
||||
// EndpointNEQ applies the NEQ predicate on the "endpoint" field.
|
||||
func EndpointNEQ(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldNEQ(FieldEndpoint, v))
|
||||
}
|
||||
|
||||
// EndpointIn applies the In predicate on the "endpoint" field.
|
||||
func EndpointIn(vs ...string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldIn(FieldEndpoint, vs...))
|
||||
}
|
||||
|
||||
// EndpointNotIn applies the NotIn predicate on the "endpoint" field.
|
||||
func EndpointNotIn(vs ...string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldNotIn(FieldEndpoint, vs...))
|
||||
}
|
||||
|
||||
// EndpointGT applies the GT predicate on the "endpoint" field.
|
||||
func EndpointGT(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldGT(FieldEndpoint, v))
|
||||
}
|
||||
|
||||
// EndpointGTE applies the GTE predicate on the "endpoint" field.
|
||||
func EndpointGTE(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldGTE(FieldEndpoint, v))
|
||||
}
|
||||
|
||||
// EndpointLT applies the LT predicate on the "endpoint" field.
|
||||
func EndpointLT(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldLT(FieldEndpoint, v))
|
||||
}
|
||||
|
||||
// EndpointLTE applies the LTE predicate on the "endpoint" field.
|
||||
func EndpointLTE(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldLTE(FieldEndpoint, v))
|
||||
}
|
||||
|
||||
// EndpointContains applies the Contains predicate on the "endpoint" field.
|
||||
func EndpointContains(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldContains(FieldEndpoint, v))
|
||||
}
|
||||
|
||||
// EndpointHasPrefix applies the HasPrefix predicate on the "endpoint" field.
|
||||
func EndpointHasPrefix(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldHasPrefix(FieldEndpoint, v))
|
||||
}
|
||||
|
||||
// EndpointHasSuffix applies the HasSuffix predicate on the "endpoint" field.
|
||||
func EndpointHasSuffix(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldHasSuffix(FieldEndpoint, v))
|
||||
}
|
||||
|
||||
// EndpointEqualFold applies the EqualFold predicate on the "endpoint" field.
|
||||
func EndpointEqualFold(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldEqualFold(FieldEndpoint, v))
|
||||
}
|
||||
|
||||
// EndpointContainsFold applies the ContainsFold predicate on the "endpoint" field.
|
||||
func EndpointContainsFold(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldContainsFold(FieldEndpoint, v))
|
||||
}
|
||||
|
||||
// PriorityEQ applies the EQ predicate on the "priority" field.
|
||||
func PriorityEQ(v int) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldEQ(FieldPriority, v))
|
||||
}
|
||||
|
||||
// PriorityNEQ applies the NEQ predicate on the "priority" field.
|
||||
func PriorityNEQ(v int) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldNEQ(FieldPriority, v))
|
||||
}
|
||||
|
||||
// PriorityIn applies the In predicate on the "priority" field.
|
||||
func PriorityIn(vs ...int) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldIn(FieldPriority, vs...))
|
||||
}
|
||||
|
||||
// PriorityNotIn applies the NotIn predicate on the "priority" field.
|
||||
func PriorityNotIn(vs ...int) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldNotIn(FieldPriority, vs...))
|
||||
}
|
||||
|
||||
// PriorityGT applies the GT predicate on the "priority" field.
|
||||
func PriorityGT(v int) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldGT(FieldPriority, v))
|
||||
}
|
||||
|
||||
// PriorityGTE applies the GTE predicate on the "priority" field.
|
||||
func PriorityGTE(v int) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldGTE(FieldPriority, v))
|
||||
}
|
||||
|
||||
// PriorityLT applies the LT predicate on the "priority" field.
|
||||
func PriorityLT(v int) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldLT(FieldPriority, v))
|
||||
}
|
||||
|
||||
// PriorityLTE applies the LTE predicate on the "priority" field.
|
||||
func PriorityLTE(v int) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldLTE(FieldPriority, v))
|
||||
}
|
||||
|
||||
// EnabledEQ applies the EQ predicate on the "enabled" field.
|
||||
func EnabledEQ(v bool) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldEQ(FieldEnabled, v))
|
||||
}
|
||||
|
||||
// EnabledNEQ applies the NEQ predicate on the "enabled" field.
|
||||
func EnabledNEQ(v bool) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldNEQ(FieldEnabled, v))
|
||||
}
|
||||
|
||||
// NotesEQ applies the EQ predicate on the "notes" field.
|
||||
func NotesEQ(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldEQ(FieldNotes, v))
|
||||
}
|
||||
|
||||
// NotesNEQ applies the NEQ predicate on the "notes" field.
|
||||
func NotesNEQ(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldNEQ(FieldNotes, v))
|
||||
}
|
||||
|
||||
// NotesIn applies the In predicate on the "notes" field.
|
||||
func NotesIn(vs ...string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldIn(FieldNotes, vs...))
|
||||
}
|
||||
|
||||
// NotesNotIn applies the NotIn predicate on the "notes" field.
|
||||
func NotesNotIn(vs ...string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldNotIn(FieldNotes, vs...))
|
||||
}
|
||||
|
||||
// NotesGT applies the GT predicate on the "notes" field.
|
||||
func NotesGT(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldGT(FieldNotes, v))
|
||||
}
|
||||
|
||||
// NotesGTE applies the GTE predicate on the "notes" field.
|
||||
func NotesGTE(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldGTE(FieldNotes, v))
|
||||
}
|
||||
|
||||
// NotesLT applies the LT predicate on the "notes" field.
|
||||
func NotesLT(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldLT(FieldNotes, v))
|
||||
}
|
||||
|
||||
// NotesLTE applies the LTE predicate on the "notes" field.
|
||||
func NotesLTE(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldLTE(FieldNotes, v))
|
||||
}
|
||||
|
||||
// NotesContains applies the Contains predicate on the "notes" field.
|
||||
func NotesContains(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldContains(FieldNotes, v))
|
||||
}
|
||||
|
||||
// NotesHasPrefix applies the HasPrefix predicate on the "notes" field.
|
||||
func NotesHasPrefix(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldHasPrefix(FieldNotes, v))
|
||||
}
|
||||
|
||||
// NotesHasSuffix applies the HasSuffix predicate on the "notes" field.
|
||||
func NotesHasSuffix(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldHasSuffix(FieldNotes, v))
|
||||
}
|
||||
|
||||
// NotesIsNil applies the IsNil predicate on the "notes" field.
|
||||
func NotesIsNil() predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldIsNull(FieldNotes))
|
||||
}
|
||||
|
||||
// NotesNotNil applies the NotNil predicate on the "notes" field.
|
||||
func NotesNotNil() predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldNotNull(FieldNotes))
|
||||
}
|
||||
|
||||
// NotesEqualFold applies the EqualFold predicate on the "notes" field.
|
||||
func NotesEqualFold(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldEqualFold(FieldNotes, v))
|
||||
}
|
||||
|
||||
// NotesContainsFold applies the ContainsFold predicate on the "notes" field.
|
||||
func NotesContainsFold(v string) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.FieldContainsFold(FieldNotes, v))
|
||||
}
|
||||
|
||||
// HasGroup applies the HasEdge predicate on the "group" edge.
|
||||
func HasGroup() predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(func(s *sql.Selector) {
|
||||
step := sqlgraph.NewStep(
|
||||
sqlgraph.From(Table, FieldID),
|
||||
sqlgraph.Edge(sqlgraph.M2O, false, GroupTable, GroupColumn),
|
||||
)
|
||||
sqlgraph.HasNeighbors(s, step)
|
||||
})
|
||||
}
|
||||
|
||||
// HasGroupWith applies the HasEdge predicate on the "group" edge with a given conditions (other predicates).
|
||||
func HasGroupWith(preds ...predicate.Group) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(func(s *sql.Selector) {
|
||||
step := newGroupStep()
|
||||
sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
|
||||
for _, p := range preds {
|
||||
p(s)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// And groups predicates with the AND operator between them.
|
||||
func And(predicates ...predicate.CompositeModelRoute) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.AndPredicates(predicates...))
|
||||
}
|
||||
|
||||
// Or groups predicates with the OR operator between them.
|
||||
func Or(predicates ...predicate.CompositeModelRoute) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.OrPredicates(predicates...))
|
||||
}
|
||||
|
||||
// Not applies the not operator on the given predicate.
|
||||
func Not(p predicate.CompositeModelRoute) predicate.CompositeModelRoute {
|
||||
return predicate.CompositeModelRoute(sql.NotPredicates(p))
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,88 @@
|
||||
// Code generated by ent, DO NOT EDIT.
|
||||
|
||||
package ent
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"entgo.io/ent/schema/field"
|
||||
"github.com/Wei-Shaw/sub2api/ent/compositemodelroute"
|
||||
"github.com/Wei-Shaw/sub2api/ent/predicate"
|
||||
)
|
||||
|
||||
// CompositeModelRouteDelete is the builder for deleting a CompositeModelRoute entity.
|
||||
type CompositeModelRouteDelete struct {
|
||||
config
|
||||
hooks []Hook
|
||||
mutation *CompositeModelRouteMutation
|
||||
}
|
||||
|
||||
// Where appends a list predicates to the CompositeModelRouteDelete builder.
|
||||
func (_d *CompositeModelRouteDelete) Where(ps ...predicate.CompositeModelRoute) *CompositeModelRouteDelete {
|
||||
_d.mutation.Where(ps...)
|
||||
return _d
|
||||
}
|
||||
|
||||
// Exec executes the deletion query and returns how many vertices were deleted.
|
||||
func (_d *CompositeModelRouteDelete) Exec(ctx context.Context) (int, error) {
|
||||
return withHooks(ctx, _d.sqlExec, _d.mutation, _d.hooks)
|
||||
}
|
||||
|
||||
// ExecX is like Exec, but panics if an error occurs.
|
||||
func (_d *CompositeModelRouteDelete) ExecX(ctx context.Context) int {
|
||||
n, err := _d.Exec(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (_d *CompositeModelRouteDelete) sqlExec(ctx context.Context) (int, error) {
|
||||
_spec := sqlgraph.NewDeleteSpec(compositemodelroute.Table, sqlgraph.NewFieldSpec(compositemodelroute.FieldID, field.TypeInt64))
|
||||
if ps := _d.mutation.predicates; len(ps) > 0 {
|
||||
_spec.Predicate = func(selector *sql.Selector) {
|
||||
for i := range ps {
|
||||
ps[i](selector)
|
||||
}
|
||||
}
|
||||
}
|
||||
affected, err := sqlgraph.DeleteNodes(ctx, _d.driver, _spec)
|
||||
if err != nil && sqlgraph.IsConstraintError(err) {
|
||||
err = &ConstraintError{msg: err.Error(), wrap: err}
|
||||
}
|
||||
_d.mutation.done = true
|
||||
return affected, err
|
||||
}
|
||||
|
||||
// CompositeModelRouteDeleteOne is the builder for deleting a single CompositeModelRoute entity.
|
||||
type CompositeModelRouteDeleteOne struct {
|
||||
_d *CompositeModelRouteDelete
|
||||
}
|
||||
|
||||
// Where appends a list predicates to the CompositeModelRouteDelete builder.
|
||||
func (_d *CompositeModelRouteDeleteOne) Where(ps ...predicate.CompositeModelRoute) *CompositeModelRouteDeleteOne {
|
||||
_d._d.mutation.Where(ps...)
|
||||
return _d
|
||||
}
|
||||
|
||||
// Exec executes the deletion query.
|
||||
func (_d *CompositeModelRouteDeleteOne) Exec(ctx context.Context) error {
|
||||
n, err := _d._d.Exec(ctx)
|
||||
switch {
|
||||
case err != nil:
|
||||
return err
|
||||
case n == 0:
|
||||
return &NotFoundError{compositemodelroute.Label}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// ExecX is like Exec, but panics if an error occurs.
|
||||
func (_d *CompositeModelRouteDeleteOne) ExecX(ctx context.Context) {
|
||||
if err := _d.Exec(ctx); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,643 @@
|
||||
// Code generated by ent, DO NOT EDIT.
|
||||
|
||||
package ent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
"entgo.io/ent"
|
||||
"entgo.io/ent/dialect"
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"entgo.io/ent/schema/field"
|
||||
"github.com/Wei-Shaw/sub2api/ent/compositemodelroute"
|
||||
"github.com/Wei-Shaw/sub2api/ent/group"
|
||||
"github.com/Wei-Shaw/sub2api/ent/predicate"
|
||||
)
|
||||
|
||||
// CompositeModelRouteQuery is the builder for querying CompositeModelRoute entities.
|
||||
type CompositeModelRouteQuery struct {
|
||||
config
|
||||
ctx *QueryContext
|
||||
order []compositemodelroute.OrderOption
|
||||
inters []Interceptor
|
||||
predicates []predicate.CompositeModelRoute
|
||||
withGroup *GroupQuery
|
||||
modifiers []func(*sql.Selector)
|
||||
// intermediate query (i.e. traversal path).
|
||||
sql *sql.Selector
|
||||
path func(context.Context) (*sql.Selector, error)
|
||||
}
|
||||
|
||||
// Where adds a new predicate for the CompositeModelRouteQuery builder.
|
||||
func (_q *CompositeModelRouteQuery) Where(ps ...predicate.CompositeModelRoute) *CompositeModelRouteQuery {
|
||||
_q.predicates = append(_q.predicates, ps...)
|
||||
return _q
|
||||
}
|
||||
|
||||
// Limit the number of records to be returned by this query.
|
||||
func (_q *CompositeModelRouteQuery) Limit(limit int) *CompositeModelRouteQuery {
|
||||
_q.ctx.Limit = &limit
|
||||
return _q
|
||||
}
|
||||
|
||||
// Offset to start from.
|
||||
func (_q *CompositeModelRouteQuery) Offset(offset int) *CompositeModelRouteQuery {
|
||||
_q.ctx.Offset = &offset
|
||||
return _q
|
||||
}
|
||||
|
||||
// Unique configures the query builder to filter duplicate records on query.
|
||||
// By default, unique is set to true, and can be disabled using this method.
|
||||
func (_q *CompositeModelRouteQuery) Unique(unique bool) *CompositeModelRouteQuery {
|
||||
_q.ctx.Unique = &unique
|
||||
return _q
|
||||
}
|
||||
|
||||
// Order specifies how the records should be ordered.
|
||||
func (_q *CompositeModelRouteQuery) Order(o ...compositemodelroute.OrderOption) *CompositeModelRouteQuery {
|
||||
_q.order = append(_q.order, o...)
|
||||
return _q
|
||||
}
|
||||
|
||||
// QueryGroup chains the current query on the "group" edge.
|
||||
func (_q *CompositeModelRouteQuery) QueryGroup() *GroupQuery {
|
||||
query := (&GroupClient{config: _q.config}).Query()
|
||||
query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
|
||||
if err := _q.prepareQuery(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
selector := _q.sqlQuery(ctx)
|
||||
if err := selector.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
step := sqlgraph.NewStep(
|
||||
sqlgraph.From(compositemodelroute.Table, compositemodelroute.FieldID, selector),
|
||||
sqlgraph.To(group.Table, group.FieldID),
|
||||
sqlgraph.Edge(sqlgraph.M2O, false, compositemodelroute.GroupTable, compositemodelroute.GroupColumn),
|
||||
)
|
||||
fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step)
|
||||
return fromU, nil
|
||||
}
|
||||
return query
|
||||
}
|
||||
|
||||
// First returns the first CompositeModelRoute entity from the query.
|
||||
// Returns a *NotFoundError when no CompositeModelRoute was found.
|
||||
func (_q *CompositeModelRouteQuery) First(ctx context.Context) (*CompositeModelRoute, error) {
|
||||
nodes, err := _q.Limit(1).All(setContextOp(ctx, _q.ctx, ent.OpQueryFirst))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(nodes) == 0 {
|
||||
return nil, &NotFoundError{compositemodelroute.Label}
|
||||
}
|
||||
return nodes[0], nil
|
||||
}
|
||||
|
||||
// FirstX is like First, but panics if an error occurs.
|
||||
func (_q *CompositeModelRouteQuery) FirstX(ctx context.Context) *CompositeModelRoute {
|
||||
node, err := _q.First(ctx)
|
||||
if err != nil && !IsNotFound(err) {
|
||||
panic(err)
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
||||
// FirstID returns the first CompositeModelRoute ID from the query.
|
||||
// Returns a *NotFoundError when no CompositeModelRoute ID was found.
|
||||
func (_q *CompositeModelRouteQuery) FirstID(ctx context.Context) (id int64, err error) {
|
||||
var ids []int64
|
||||
if ids, err = _q.Limit(1).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryFirstID)); err != nil {
|
||||
return
|
||||
}
|
||||
if len(ids) == 0 {
|
||||
err = &NotFoundError{compositemodelroute.Label}
|
||||
return
|
||||
}
|
||||
return ids[0], nil
|
||||
}
|
||||
|
||||
// FirstIDX is like FirstID, but panics if an error occurs.
|
||||
func (_q *CompositeModelRouteQuery) FirstIDX(ctx context.Context) int64 {
|
||||
id, err := _q.FirstID(ctx)
|
||||
if err != nil && !IsNotFound(err) {
|
||||
panic(err)
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
// Only returns a single CompositeModelRoute entity found by the query, ensuring it only returns one.
|
||||
// Returns a *NotSingularError when more than one CompositeModelRoute entity is found.
|
||||
// Returns a *NotFoundError when no CompositeModelRoute entities are found.
|
||||
func (_q *CompositeModelRouteQuery) Only(ctx context.Context) (*CompositeModelRoute, error) {
|
||||
nodes, err := _q.Limit(2).All(setContextOp(ctx, _q.ctx, ent.OpQueryOnly))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch len(nodes) {
|
||||
case 1:
|
||||
return nodes[0], nil
|
||||
case 0:
|
||||
return nil, &NotFoundError{compositemodelroute.Label}
|
||||
default:
|
||||
return nil, &NotSingularError{compositemodelroute.Label}
|
||||
}
|
||||
}
|
||||
|
||||
// OnlyX is like Only, but panics if an error occurs.
|
||||
func (_q *CompositeModelRouteQuery) OnlyX(ctx context.Context) *CompositeModelRoute {
|
||||
node, err := _q.Only(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
||||
// OnlyID is like Only, but returns the only CompositeModelRoute ID in the query.
|
||||
// Returns a *NotSingularError when more than one CompositeModelRoute ID is found.
|
||||
// Returns a *NotFoundError when no entities are found.
|
||||
func (_q *CompositeModelRouteQuery) OnlyID(ctx context.Context) (id int64, err error) {
|
||||
var ids []int64
|
||||
if ids, err = _q.Limit(2).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryOnlyID)); err != nil {
|
||||
return
|
||||
}
|
||||
switch len(ids) {
|
||||
case 1:
|
||||
id = ids[0]
|
||||
case 0:
|
||||
err = &NotFoundError{compositemodelroute.Label}
|
||||
default:
|
||||
err = &NotSingularError{compositemodelroute.Label}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// OnlyIDX is like OnlyID, but panics if an error occurs.
|
||||
func (_q *CompositeModelRouteQuery) OnlyIDX(ctx context.Context) int64 {
|
||||
id, err := _q.OnlyID(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
// All executes the query and returns a list of CompositeModelRoutes.
|
||||
func (_q *CompositeModelRouteQuery) All(ctx context.Context) ([]*CompositeModelRoute, error) {
|
||||
ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll)
|
||||
if err := _q.prepareQuery(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
qr := querierAll[[]*CompositeModelRoute, *CompositeModelRouteQuery]()
|
||||
return withInterceptors[[]*CompositeModelRoute](ctx, _q, qr, _q.inters)
|
||||
}
|
||||
|
||||
// AllX is like All, but panics if an error occurs.
|
||||
func (_q *CompositeModelRouteQuery) AllX(ctx context.Context) []*CompositeModelRoute {
|
||||
nodes, err := _q.All(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return nodes
|
||||
}
|
||||
|
||||
// IDs executes the query and returns a list of CompositeModelRoute IDs.
|
||||
func (_q *CompositeModelRouteQuery) IDs(ctx context.Context) (ids []int64, err error) {
|
||||
if _q.ctx.Unique == nil && _q.path != nil {
|
||||
_q.Unique(true)
|
||||
}
|
||||
ctx = setContextOp(ctx, _q.ctx, ent.OpQueryIDs)
|
||||
if err = _q.Select(compositemodelroute.FieldID).Scan(ctx, &ids); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
// IDsX is like IDs, but panics if an error occurs.
|
||||
func (_q *CompositeModelRouteQuery) IDsX(ctx context.Context) []int64 {
|
||||
ids, err := _q.IDs(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
// Count returns the count of the given query.
|
||||
func (_q *CompositeModelRouteQuery) Count(ctx context.Context) (int, error) {
|
||||
ctx = setContextOp(ctx, _q.ctx, ent.OpQueryCount)
|
||||
if err := _q.prepareQuery(ctx); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return withInterceptors[int](ctx, _q, querierCount[*CompositeModelRouteQuery](), _q.inters)
|
||||
}
|
||||
|
||||
// CountX is like Count, but panics if an error occurs.
|
||||
func (_q *CompositeModelRouteQuery) CountX(ctx context.Context) int {
|
||||
count, err := _q.Count(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
// Exist returns true if the query has elements in the graph.
|
||||
func (_q *CompositeModelRouteQuery) Exist(ctx context.Context) (bool, error) {
|
||||
ctx = setContextOp(ctx, _q.ctx, ent.OpQueryExist)
|
||||
switch _, err := _q.FirstID(ctx); {
|
||||
case IsNotFound(err):
|
||||
return false, nil
|
||||
case err != nil:
|
||||
return false, fmt.Errorf("ent: check existence: %w", err)
|
||||
default:
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
// ExistX is like Exist, but panics if an error occurs.
|
||||
func (_q *CompositeModelRouteQuery) ExistX(ctx context.Context) bool {
|
||||
exist, err := _q.Exist(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return exist
|
||||
}
|
||||
|
||||
// Clone returns a duplicate of the CompositeModelRouteQuery builder, including all associated steps. It can be
|
||||
// used to prepare common query builders and use them differently after the clone is made.
|
||||
func (_q *CompositeModelRouteQuery) Clone() *CompositeModelRouteQuery {
|
||||
if _q == nil {
|
||||
return nil
|
||||
}
|
||||
return &CompositeModelRouteQuery{
|
||||
config: _q.config,
|
||||
ctx: _q.ctx.Clone(),
|
||||
order: append([]compositemodelroute.OrderOption{}, _q.order...),
|
||||
inters: append([]Interceptor{}, _q.inters...),
|
||||
predicates: append([]predicate.CompositeModelRoute{}, _q.predicates...),
|
||||
withGroup: _q.withGroup.Clone(),
|
||||
// clone intermediate query.
|
||||
sql: _q.sql.Clone(),
|
||||
path: _q.path,
|
||||
}
|
||||
}
|
||||
|
||||
// WithGroup tells the query-builder to eager-load the nodes that are connected to
|
||||
// the "group" edge. The optional arguments are used to configure the query builder of the edge.
|
||||
func (_q *CompositeModelRouteQuery) WithGroup(opts ...func(*GroupQuery)) *CompositeModelRouteQuery {
|
||||
query := (&GroupClient{config: _q.config}).Query()
|
||||
for _, opt := range opts {
|
||||
opt(query)
|
||||
}
|
||||
_q.withGroup = query
|
||||
return _q
|
||||
}
|
||||
|
||||
// GroupBy is used to group vertices by one or more fields/columns.
|
||||
// It is often used with aggregate functions, like: count, max, mean, min, sum.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// var v []struct {
|
||||
// CreatedAt time.Time `json:"created_at,omitempty"`
|
||||
// Count int `json:"count,omitempty"`
|
||||
// }
|
||||
//
|
||||
// client.CompositeModelRoute.Query().
|
||||
// GroupBy(compositemodelroute.FieldCreatedAt).
|
||||
// Aggregate(ent.Count()).
|
||||
// Scan(ctx, &v)
|
||||
func (_q *CompositeModelRouteQuery) GroupBy(field string, fields ...string) *CompositeModelRouteGroupBy {
|
||||
_q.ctx.Fields = append([]string{field}, fields...)
|
||||
grbuild := &CompositeModelRouteGroupBy{build: _q}
|
||||
grbuild.flds = &_q.ctx.Fields
|
||||
grbuild.label = compositemodelroute.Label
|
||||
grbuild.scan = grbuild.Scan
|
||||
return grbuild
|
||||
}
|
||||
|
||||
// Select allows the selection one or more fields/columns for the given query,
|
||||
// instead of selecting all fields in the entity.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// var v []struct {
|
||||
// CreatedAt time.Time `json:"created_at,omitempty"`
|
||||
// }
|
||||
//
|
||||
// client.CompositeModelRoute.Query().
|
||||
// Select(compositemodelroute.FieldCreatedAt).
|
||||
// Scan(ctx, &v)
|
||||
func (_q *CompositeModelRouteQuery) Select(fields ...string) *CompositeModelRouteSelect {
|
||||
_q.ctx.Fields = append(_q.ctx.Fields, fields...)
|
||||
sbuild := &CompositeModelRouteSelect{CompositeModelRouteQuery: _q}
|
||||
sbuild.label = compositemodelroute.Label
|
||||
sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan
|
||||
return sbuild
|
||||
}
|
||||
|
||||
// Aggregate returns a CompositeModelRouteSelect configured with the given aggregations.
|
||||
func (_q *CompositeModelRouteQuery) Aggregate(fns ...AggregateFunc) *CompositeModelRouteSelect {
|
||||
return _q.Select().Aggregate(fns...)
|
||||
}
|
||||
|
||||
func (_q *CompositeModelRouteQuery) prepareQuery(ctx context.Context) error {
|
||||
for _, inter := range _q.inters {
|
||||
if inter == nil {
|
||||
return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)")
|
||||
}
|
||||
if trv, ok := inter.(Traverser); ok {
|
||||
if err := trv.Traverse(ctx, _q); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, f := range _q.ctx.Fields {
|
||||
if !compositemodelroute.ValidColumn(f) {
|
||||
return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
|
||||
}
|
||||
}
|
||||
if _q.path != nil {
|
||||
prev, err := _q.path(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_q.sql = prev
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_q *CompositeModelRouteQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*CompositeModelRoute, error) {
|
||||
var (
|
||||
nodes = []*CompositeModelRoute{}
|
||||
_spec = _q.querySpec()
|
||||
loadedTypes = [1]bool{
|
||||
_q.withGroup != nil,
|
||||
}
|
||||
)
|
||||
_spec.ScanValues = func(columns []string) ([]any, error) {
|
||||
return (*CompositeModelRoute).scanValues(nil, columns)
|
||||
}
|
||||
_spec.Assign = func(columns []string, values []any) error {
|
||||
node := &CompositeModelRoute{config: _q.config}
|
||||
nodes = append(nodes, node)
|
||||
node.Edges.loadedTypes = loadedTypes
|
||||
return node.assignValues(columns, values)
|
||||
}
|
||||
if len(_q.modifiers) > 0 {
|
||||
_spec.Modifiers = _q.modifiers
|
||||
}
|
||||
for i := range hooks {
|
||||
hooks[i](ctx, _spec)
|
||||
}
|
||||
if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(nodes) == 0 {
|
||||
return nodes, nil
|
||||
}
|
||||
if query := _q.withGroup; query != nil {
|
||||
if err := _q.loadGroup(ctx, query, nodes, nil,
|
||||
func(n *CompositeModelRoute, e *Group) { n.Edges.Group = e }); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return nodes, nil
|
||||
}
|
||||
|
||||
func (_q *CompositeModelRouteQuery) loadGroup(ctx context.Context, query *GroupQuery, nodes []*CompositeModelRoute, init func(*CompositeModelRoute), assign func(*CompositeModelRoute, *Group)) error {
|
||||
ids := make([]int64, 0, len(nodes))
|
||||
nodeids := make(map[int64][]*CompositeModelRoute)
|
||||
for i := range nodes {
|
||||
fk := nodes[i].GroupID
|
||||
if _, ok := nodeids[fk]; !ok {
|
||||
ids = append(ids, fk)
|
||||
}
|
||||
nodeids[fk] = append(nodeids[fk], nodes[i])
|
||||
}
|
||||
if len(ids) == 0 {
|
||||
return nil
|
||||
}
|
||||
query.Where(group.IDIn(ids...))
|
||||
neighbors, err := query.All(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, n := range neighbors {
|
||||
nodes, ok := nodeids[n.ID]
|
||||
if !ok {
|
||||
return fmt.Errorf(`unexpected foreign-key "group_id" returned %v`, n.ID)
|
||||
}
|
||||
for i := range nodes {
|
||||
assign(nodes[i], n)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_q *CompositeModelRouteQuery) sqlCount(ctx context.Context) (int, error) {
|
||||
_spec := _q.querySpec()
|
||||
if len(_q.modifiers) > 0 {
|
||||
_spec.Modifiers = _q.modifiers
|
||||
}
|
||||
_spec.Node.Columns = _q.ctx.Fields
|
||||
if len(_q.ctx.Fields) > 0 {
|
||||
_spec.Unique = _q.ctx.Unique != nil && *_q.ctx.Unique
|
||||
}
|
||||
return sqlgraph.CountNodes(ctx, _q.driver, _spec)
|
||||
}
|
||||
|
||||
func (_q *CompositeModelRouteQuery) querySpec() *sqlgraph.QuerySpec {
|
||||
_spec := sqlgraph.NewQuerySpec(compositemodelroute.Table, compositemodelroute.Columns, sqlgraph.NewFieldSpec(compositemodelroute.FieldID, field.TypeInt64))
|
||||
_spec.From = _q.sql
|
||||
if unique := _q.ctx.Unique; unique != nil {
|
||||
_spec.Unique = *unique
|
||||
} else if _q.path != nil {
|
||||
_spec.Unique = true
|
||||
}
|
||||
if fields := _q.ctx.Fields; len(fields) > 0 {
|
||||
_spec.Node.Columns = make([]string, 0, len(fields))
|
||||
_spec.Node.Columns = append(_spec.Node.Columns, compositemodelroute.FieldID)
|
||||
for i := range fields {
|
||||
if fields[i] != compositemodelroute.FieldID {
|
||||
_spec.Node.Columns = append(_spec.Node.Columns, fields[i])
|
||||
}
|
||||
}
|
||||
if _q.withGroup != nil {
|
||||
_spec.Node.AddColumnOnce(compositemodelroute.FieldGroupID)
|
||||
}
|
||||
}
|
||||
if ps := _q.predicates; len(ps) > 0 {
|
||||
_spec.Predicate = func(selector *sql.Selector) {
|
||||
for i := range ps {
|
||||
ps[i](selector)
|
||||
}
|
||||
}
|
||||
}
|
||||
if limit := _q.ctx.Limit; limit != nil {
|
||||
_spec.Limit = *limit
|
||||
}
|
||||
if offset := _q.ctx.Offset; offset != nil {
|
||||
_spec.Offset = *offset
|
||||
}
|
||||
if ps := _q.order; len(ps) > 0 {
|
||||
_spec.Order = func(selector *sql.Selector) {
|
||||
for i := range ps {
|
||||
ps[i](selector)
|
||||
}
|
||||
}
|
||||
}
|
||||
return _spec
|
||||
}
|
||||
|
||||
func (_q *CompositeModelRouteQuery) sqlQuery(ctx context.Context) *sql.Selector {
|
||||
builder := sql.Dialect(_q.driver.Dialect())
|
||||
t1 := builder.Table(compositemodelroute.Table)
|
||||
columns := _q.ctx.Fields
|
||||
if len(columns) == 0 {
|
||||
columns = compositemodelroute.Columns
|
||||
}
|
||||
selector := builder.Select(t1.Columns(columns...)...).From(t1)
|
||||
if _q.sql != nil {
|
||||
selector = _q.sql
|
||||
selector.Select(selector.Columns(columns...)...)
|
||||
}
|
||||
if _q.ctx.Unique != nil && *_q.ctx.Unique {
|
||||
selector.Distinct()
|
||||
}
|
||||
for _, m := range _q.modifiers {
|
||||
m(selector)
|
||||
}
|
||||
for _, p := range _q.predicates {
|
||||
p(selector)
|
||||
}
|
||||
for _, p := range _q.order {
|
||||
p(selector)
|
||||
}
|
||||
if offset := _q.ctx.Offset; offset != nil {
|
||||
// limit is mandatory for offset clause. We start
|
||||
// with default value, and override it below if needed.
|
||||
selector.Offset(*offset).Limit(math.MaxInt32)
|
||||
}
|
||||
if limit := _q.ctx.Limit; limit != nil {
|
||||
selector.Limit(*limit)
|
||||
}
|
||||
return selector
|
||||
}
|
||||
|
||||
// ForUpdate locks the selected rows against concurrent updates, and prevent them from being
|
||||
// updated, deleted or "selected ... for update" by other sessions, until the transaction is
|
||||
// either committed or rolled-back.
|
||||
func (_q *CompositeModelRouteQuery) ForUpdate(opts ...sql.LockOption) *CompositeModelRouteQuery {
|
||||
if _q.driver.Dialect() == dialect.Postgres {
|
||||
_q.Unique(false)
|
||||
}
|
||||
_q.modifiers = append(_q.modifiers, func(s *sql.Selector) {
|
||||
s.ForUpdate(opts...)
|
||||
})
|
||||
return _q
|
||||
}
|
||||
|
||||
// ForShare behaves similarly to ForUpdate, except that it acquires a shared mode lock
|
||||
// on any rows that are read. Other sessions can read the rows, but cannot modify them
|
||||
// until your transaction commits.
|
||||
func (_q *CompositeModelRouteQuery) ForShare(opts ...sql.LockOption) *CompositeModelRouteQuery {
|
||||
if _q.driver.Dialect() == dialect.Postgres {
|
||||
_q.Unique(false)
|
||||
}
|
||||
_q.modifiers = append(_q.modifiers, func(s *sql.Selector) {
|
||||
s.ForShare(opts...)
|
||||
})
|
||||
return _q
|
||||
}
|
||||
|
||||
// CompositeModelRouteGroupBy is the group-by builder for CompositeModelRoute entities.
|
||||
type CompositeModelRouteGroupBy struct {
|
||||
selector
|
||||
build *CompositeModelRouteQuery
|
||||
}
|
||||
|
||||
// Aggregate adds the given aggregation functions to the group-by query.
|
||||
func (_g *CompositeModelRouteGroupBy) Aggregate(fns ...AggregateFunc) *CompositeModelRouteGroupBy {
|
||||
_g.fns = append(_g.fns, fns...)
|
||||
return _g
|
||||
}
|
||||
|
||||
// Scan applies the selector query and scans the result into the given value.
|
||||
func (_g *CompositeModelRouteGroupBy) Scan(ctx context.Context, v any) error {
|
||||
ctx = setContextOp(ctx, _g.build.ctx, ent.OpQueryGroupBy)
|
||||
if err := _g.build.prepareQuery(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
return scanWithInterceptors[*CompositeModelRouteQuery, *CompositeModelRouteGroupBy](ctx, _g.build, _g, _g.build.inters, v)
|
||||
}
|
||||
|
||||
func (_g *CompositeModelRouteGroupBy) sqlScan(ctx context.Context, root *CompositeModelRouteQuery, v any) error {
|
||||
selector := root.sqlQuery(ctx).Select()
|
||||
aggregation := make([]string, 0, len(_g.fns))
|
||||
for _, fn := range _g.fns {
|
||||
aggregation = append(aggregation, fn(selector))
|
||||
}
|
||||
if len(selector.SelectedColumns()) == 0 {
|
||||
columns := make([]string, 0, len(*_g.flds)+len(_g.fns))
|
||||
for _, f := range *_g.flds {
|
||||
columns = append(columns, selector.C(f))
|
||||
}
|
||||
columns = append(columns, aggregation...)
|
||||
selector.Select(columns...)
|
||||
}
|
||||
selector.GroupBy(selector.Columns(*_g.flds...)...)
|
||||
if err := selector.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
rows := &sql.Rows{}
|
||||
query, args := selector.Query()
|
||||
if err := _g.build.driver.Query(ctx, query, args, rows); err != nil {
|
||||
return err
|
||||
}
|
||||
defer rows.Close()
|
||||
return sql.ScanSlice(rows, v)
|
||||
}
|
||||
|
||||
// CompositeModelRouteSelect is the builder for selecting fields of CompositeModelRoute entities.
|
||||
type CompositeModelRouteSelect struct {
|
||||
*CompositeModelRouteQuery
|
||||
selector
|
||||
}
|
||||
|
||||
// Aggregate adds the given aggregation functions to the selector query.
|
||||
func (_s *CompositeModelRouteSelect) Aggregate(fns ...AggregateFunc) *CompositeModelRouteSelect {
|
||||
_s.fns = append(_s.fns, fns...)
|
||||
return _s
|
||||
}
|
||||
|
||||
// Scan applies the selector query and scans the result into the given value.
|
||||
func (_s *CompositeModelRouteSelect) Scan(ctx context.Context, v any) error {
|
||||
ctx = setContextOp(ctx, _s.ctx, ent.OpQuerySelect)
|
||||
if err := _s.prepareQuery(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
return scanWithInterceptors[*CompositeModelRouteQuery, *CompositeModelRouteSelect](ctx, _s.CompositeModelRouteQuery, _s, _s.inters, v)
|
||||
}
|
||||
|
||||
func (_s *CompositeModelRouteSelect) sqlScan(ctx context.Context, root *CompositeModelRouteQuery, v any) error {
|
||||
selector := root.sqlQuery(ctx)
|
||||
aggregation := make([]string, 0, len(_s.fns))
|
||||
for _, fn := range _s.fns {
|
||||
aggregation = append(aggregation, fn(selector))
|
||||
}
|
||||
switch n := len(*_s.selector.flds); {
|
||||
case n == 0 && len(aggregation) > 0:
|
||||
selector.Select(aggregation...)
|
||||
case n != 0 && len(aggregation) > 0:
|
||||
selector.AppendSelect(aggregation...)
|
||||
}
|
||||
rows := &sql.Rows{}
|
||||
query, args := selector.Query()
|
||||
if err := _s.driver.Query(ctx, query, args, rows); err != nil {
|
||||
return err
|
||||
}
|
||||
defer rows.Close()
|
||||
return sql.ScanSlice(rows, v)
|
||||
}
|
||||
@@ -0,0 +1,767 @@
|
||||
// Code generated by ent, DO NOT EDIT.
|
||||
|
||||
package ent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"entgo.io/ent/schema/field"
|
||||
"github.com/Wei-Shaw/sub2api/ent/compositemodelroute"
|
||||
"github.com/Wei-Shaw/sub2api/ent/group"
|
||||
"github.com/Wei-Shaw/sub2api/ent/predicate"
|
||||
)
|
||||
|
||||
// CompositeModelRouteUpdate is the builder for updating CompositeModelRoute entities.
|
||||
type CompositeModelRouteUpdate struct {
|
||||
config
|
||||
hooks []Hook
|
||||
mutation *CompositeModelRouteMutation
|
||||
}
|
||||
|
||||
// Where appends a list predicates to the CompositeModelRouteUpdate builder.
|
||||
func (_u *CompositeModelRouteUpdate) Where(ps ...predicate.CompositeModelRoute) *CompositeModelRouteUpdate {
|
||||
_u.mutation.Where(ps...)
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetUpdatedAt sets the "updated_at" field.
|
||||
func (_u *CompositeModelRouteUpdate) SetUpdatedAt(v time.Time) *CompositeModelRouteUpdate {
|
||||
_u.mutation.SetUpdatedAt(v)
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetDeletedAt sets the "deleted_at" field.
|
||||
func (_u *CompositeModelRouteUpdate) SetDeletedAt(v time.Time) *CompositeModelRouteUpdate {
|
||||
_u.mutation.SetDeletedAt(v)
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetNillableDeletedAt sets the "deleted_at" field if the given value is not nil.
|
||||
func (_u *CompositeModelRouteUpdate) SetNillableDeletedAt(v *time.Time) *CompositeModelRouteUpdate {
|
||||
if v != nil {
|
||||
_u.SetDeletedAt(*v)
|
||||
}
|
||||
return _u
|
||||
}
|
||||
|
||||
// ClearDeletedAt clears the value of the "deleted_at" field.
|
||||
func (_u *CompositeModelRouteUpdate) ClearDeletedAt() *CompositeModelRouteUpdate {
|
||||
_u.mutation.ClearDeletedAt()
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetGroupID sets the "group_id" field.
|
||||
func (_u *CompositeModelRouteUpdate) SetGroupID(v int64) *CompositeModelRouteUpdate {
|
||||
_u.mutation.SetGroupID(v)
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetNillableGroupID sets the "group_id" field if the given value is not nil.
|
||||
func (_u *CompositeModelRouteUpdate) SetNillableGroupID(v *int64) *CompositeModelRouteUpdate {
|
||||
if v != nil {
|
||||
_u.SetGroupID(*v)
|
||||
}
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetPublicModel sets the "public_model" field.
|
||||
func (_u *CompositeModelRouteUpdate) SetPublicModel(v string) *CompositeModelRouteUpdate {
|
||||
_u.mutation.SetPublicModel(v)
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetNillablePublicModel sets the "public_model" field if the given value is not nil.
|
||||
func (_u *CompositeModelRouteUpdate) SetNillablePublicModel(v *string) *CompositeModelRouteUpdate {
|
||||
if v != nil {
|
||||
_u.SetPublicModel(*v)
|
||||
}
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetMatchType sets the "match_type" field.
|
||||
func (_u *CompositeModelRouteUpdate) SetMatchType(v string) *CompositeModelRouteUpdate {
|
||||
_u.mutation.SetMatchType(v)
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetNillableMatchType sets the "match_type" field if the given value is not nil.
|
||||
func (_u *CompositeModelRouteUpdate) SetNillableMatchType(v *string) *CompositeModelRouteUpdate {
|
||||
if v != nil {
|
||||
_u.SetMatchType(*v)
|
||||
}
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetTargetPlatform sets the "target_platform" field.
|
||||
func (_u *CompositeModelRouteUpdate) SetTargetPlatform(v string) *CompositeModelRouteUpdate {
|
||||
_u.mutation.SetTargetPlatform(v)
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetNillableTargetPlatform sets the "target_platform" field if the given value is not nil.
|
||||
func (_u *CompositeModelRouteUpdate) SetNillableTargetPlatform(v *string) *CompositeModelRouteUpdate {
|
||||
if v != nil {
|
||||
_u.SetTargetPlatform(*v)
|
||||
}
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetUpstreamModel sets the "upstream_model" field.
|
||||
func (_u *CompositeModelRouteUpdate) SetUpstreamModel(v string) *CompositeModelRouteUpdate {
|
||||
_u.mutation.SetUpstreamModel(v)
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetNillableUpstreamModel sets the "upstream_model" field if the given value is not nil.
|
||||
func (_u *CompositeModelRouteUpdate) SetNillableUpstreamModel(v *string) *CompositeModelRouteUpdate {
|
||||
if v != nil {
|
||||
_u.SetUpstreamModel(*v)
|
||||
}
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetEndpoint sets the "endpoint" field.
|
||||
func (_u *CompositeModelRouteUpdate) SetEndpoint(v string) *CompositeModelRouteUpdate {
|
||||
_u.mutation.SetEndpoint(v)
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetNillableEndpoint sets the "endpoint" field if the given value is not nil.
|
||||
func (_u *CompositeModelRouteUpdate) SetNillableEndpoint(v *string) *CompositeModelRouteUpdate {
|
||||
if v != nil {
|
||||
_u.SetEndpoint(*v)
|
||||
}
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetPriority sets the "priority" field.
|
||||
func (_u *CompositeModelRouteUpdate) SetPriority(v int) *CompositeModelRouteUpdate {
|
||||
_u.mutation.ResetPriority()
|
||||
_u.mutation.SetPriority(v)
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetNillablePriority sets the "priority" field if the given value is not nil.
|
||||
func (_u *CompositeModelRouteUpdate) SetNillablePriority(v *int) *CompositeModelRouteUpdate {
|
||||
if v != nil {
|
||||
_u.SetPriority(*v)
|
||||
}
|
||||
return _u
|
||||
}
|
||||
|
||||
// AddPriority adds value to the "priority" field.
|
||||
func (_u *CompositeModelRouteUpdate) AddPriority(v int) *CompositeModelRouteUpdate {
|
||||
_u.mutation.AddPriority(v)
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetEnabled sets the "enabled" field.
|
||||
func (_u *CompositeModelRouteUpdate) SetEnabled(v bool) *CompositeModelRouteUpdate {
|
||||
_u.mutation.SetEnabled(v)
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetNillableEnabled sets the "enabled" field if the given value is not nil.
|
||||
func (_u *CompositeModelRouteUpdate) SetNillableEnabled(v *bool) *CompositeModelRouteUpdate {
|
||||
if v != nil {
|
||||
_u.SetEnabled(*v)
|
||||
}
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetNotes sets the "notes" field.
|
||||
func (_u *CompositeModelRouteUpdate) SetNotes(v string) *CompositeModelRouteUpdate {
|
||||
_u.mutation.SetNotes(v)
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetNillableNotes sets the "notes" field if the given value is not nil.
|
||||
func (_u *CompositeModelRouteUpdate) SetNillableNotes(v *string) *CompositeModelRouteUpdate {
|
||||
if v != nil {
|
||||
_u.SetNotes(*v)
|
||||
}
|
||||
return _u
|
||||
}
|
||||
|
||||
// ClearNotes clears the value of the "notes" field.
|
||||
func (_u *CompositeModelRouteUpdate) ClearNotes() *CompositeModelRouteUpdate {
|
||||
_u.mutation.ClearNotes()
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetGroup sets the "group" edge to the Group entity.
|
||||
func (_u *CompositeModelRouteUpdate) SetGroup(v *Group) *CompositeModelRouteUpdate {
|
||||
return _u.SetGroupID(v.ID)
|
||||
}
|
||||
|
||||
// Mutation returns the CompositeModelRouteMutation object of the builder.
|
||||
func (_u *CompositeModelRouteUpdate) Mutation() *CompositeModelRouteMutation {
|
||||
return _u.mutation
|
||||
}
|
||||
|
||||
// ClearGroup clears the "group" edge to the Group entity.
|
||||
func (_u *CompositeModelRouteUpdate) ClearGroup() *CompositeModelRouteUpdate {
|
||||
_u.mutation.ClearGroup()
|
||||
return _u
|
||||
}
|
||||
|
||||
// Save executes the query and returns the number of nodes affected by the update operation.
|
||||
func (_u *CompositeModelRouteUpdate) Save(ctx context.Context) (int, error) {
|
||||
if err := _u.defaults(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks)
|
||||
}
|
||||
|
||||
// SaveX is like Save, but panics if an error occurs.
|
||||
func (_u *CompositeModelRouteUpdate) SaveX(ctx context.Context) int {
|
||||
affected, err := _u.Save(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return affected
|
||||
}
|
||||
|
||||
// Exec executes the query.
|
||||
func (_u *CompositeModelRouteUpdate) Exec(ctx context.Context) error {
|
||||
_, err := _u.Save(ctx)
|
||||
return err
|
||||
}
|
||||
|
||||
// ExecX is like Exec, but panics if an error occurs.
|
||||
func (_u *CompositeModelRouteUpdate) ExecX(ctx context.Context) {
|
||||
if err := _u.Exec(ctx); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// defaults sets the default values of the builder before save.
|
||||
func (_u *CompositeModelRouteUpdate) defaults() error {
|
||||
if _, ok := _u.mutation.UpdatedAt(); !ok {
|
||||
if compositemodelroute.UpdateDefaultUpdatedAt == nil {
|
||||
return fmt.Errorf("ent: uninitialized compositemodelroute.UpdateDefaultUpdatedAt (forgotten import ent/runtime?)")
|
||||
}
|
||||
v := compositemodelroute.UpdateDefaultUpdatedAt()
|
||||
_u.mutation.SetUpdatedAt(v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// check runs all checks and user-defined validators on the builder.
|
||||
func (_u *CompositeModelRouteUpdate) check() error {
|
||||
if v, ok := _u.mutation.PublicModel(); ok {
|
||||
if err := compositemodelroute.PublicModelValidator(v); err != nil {
|
||||
return &ValidationError{Name: "public_model", err: fmt.Errorf(`ent: validator failed for field "CompositeModelRoute.public_model": %w`, err)}
|
||||
}
|
||||
}
|
||||
if v, ok := _u.mutation.MatchType(); ok {
|
||||
if err := compositemodelroute.MatchTypeValidator(v); err != nil {
|
||||
return &ValidationError{Name: "match_type", err: fmt.Errorf(`ent: validator failed for field "CompositeModelRoute.match_type": %w`, err)}
|
||||
}
|
||||
}
|
||||
if v, ok := _u.mutation.TargetPlatform(); ok {
|
||||
if err := compositemodelroute.TargetPlatformValidator(v); err != nil {
|
||||
return &ValidationError{Name: "target_platform", err: fmt.Errorf(`ent: validator failed for field "CompositeModelRoute.target_platform": %w`, err)}
|
||||
}
|
||||
}
|
||||
if v, ok := _u.mutation.UpstreamModel(); ok {
|
||||
if err := compositemodelroute.UpstreamModelValidator(v); err != nil {
|
||||
return &ValidationError{Name: "upstream_model", err: fmt.Errorf(`ent: validator failed for field "CompositeModelRoute.upstream_model": %w`, err)}
|
||||
}
|
||||
}
|
||||
if v, ok := _u.mutation.Endpoint(); ok {
|
||||
if err := compositemodelroute.EndpointValidator(v); err != nil {
|
||||
return &ValidationError{Name: "endpoint", err: fmt.Errorf(`ent: validator failed for field "CompositeModelRoute.endpoint": %w`, err)}
|
||||
}
|
||||
}
|
||||
if _u.mutation.GroupCleared() && len(_u.mutation.GroupIDs()) > 0 {
|
||||
return errors.New(`ent: clearing a required unique edge "CompositeModelRoute.group"`)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_u *CompositeModelRouteUpdate) sqlSave(ctx context.Context) (_node int, err error) {
|
||||
if err := _u.check(); err != nil {
|
||||
return _node, err
|
||||
}
|
||||
_spec := sqlgraph.NewUpdateSpec(compositemodelroute.Table, compositemodelroute.Columns, sqlgraph.NewFieldSpec(compositemodelroute.FieldID, field.TypeInt64))
|
||||
if ps := _u.mutation.predicates; len(ps) > 0 {
|
||||
_spec.Predicate = func(selector *sql.Selector) {
|
||||
for i := range ps {
|
||||
ps[i](selector)
|
||||
}
|
||||
}
|
||||
}
|
||||
if value, ok := _u.mutation.UpdatedAt(); ok {
|
||||
_spec.SetField(compositemodelroute.FieldUpdatedAt, field.TypeTime, value)
|
||||
}
|
||||
if value, ok := _u.mutation.DeletedAt(); ok {
|
||||
_spec.SetField(compositemodelroute.FieldDeletedAt, field.TypeTime, value)
|
||||
}
|
||||
if _u.mutation.DeletedAtCleared() {
|
||||
_spec.ClearField(compositemodelroute.FieldDeletedAt, field.TypeTime)
|
||||
}
|
||||
if value, ok := _u.mutation.PublicModel(); ok {
|
||||
_spec.SetField(compositemodelroute.FieldPublicModel, field.TypeString, value)
|
||||
}
|
||||
if value, ok := _u.mutation.MatchType(); ok {
|
||||
_spec.SetField(compositemodelroute.FieldMatchType, field.TypeString, value)
|
||||
}
|
||||
if value, ok := _u.mutation.TargetPlatform(); ok {
|
||||
_spec.SetField(compositemodelroute.FieldTargetPlatform, field.TypeString, value)
|
||||
}
|
||||
if value, ok := _u.mutation.UpstreamModel(); ok {
|
||||
_spec.SetField(compositemodelroute.FieldUpstreamModel, field.TypeString, value)
|
||||
}
|
||||
if value, ok := _u.mutation.Endpoint(); ok {
|
||||
_spec.SetField(compositemodelroute.FieldEndpoint, field.TypeString, value)
|
||||
}
|
||||
if value, ok := _u.mutation.Priority(); ok {
|
||||
_spec.SetField(compositemodelroute.FieldPriority, field.TypeInt, value)
|
||||
}
|
||||
if value, ok := _u.mutation.AddedPriority(); ok {
|
||||
_spec.AddField(compositemodelroute.FieldPriority, field.TypeInt, value)
|
||||
}
|
||||
if value, ok := _u.mutation.Enabled(); ok {
|
||||
_spec.SetField(compositemodelroute.FieldEnabled, field.TypeBool, value)
|
||||
}
|
||||
if value, ok := _u.mutation.Notes(); ok {
|
||||
_spec.SetField(compositemodelroute.FieldNotes, field.TypeString, value)
|
||||
}
|
||||
if _u.mutation.NotesCleared() {
|
||||
_spec.ClearField(compositemodelroute.FieldNotes, field.TypeString)
|
||||
}
|
||||
if _u.mutation.GroupCleared() {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.M2O,
|
||||
Inverse: false,
|
||||
Table: compositemodelroute.GroupTable,
|
||||
Columns: []string{compositemodelroute.GroupColumn},
|
||||
Bidi: false,
|
||||
Target: &sqlgraph.EdgeTarget{
|
||||
IDSpec: sqlgraph.NewFieldSpec(group.FieldID, field.TypeInt64),
|
||||
},
|
||||
}
|
||||
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
|
||||
}
|
||||
if nodes := _u.mutation.GroupIDs(); len(nodes) > 0 {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.M2O,
|
||||
Inverse: false,
|
||||
Table: compositemodelroute.GroupTable,
|
||||
Columns: []string{compositemodelroute.GroupColumn},
|
||||
Bidi: false,
|
||||
Target: &sqlgraph.EdgeTarget{
|
||||
IDSpec: sqlgraph.NewFieldSpec(group.FieldID, field.TypeInt64),
|
||||
},
|
||||
}
|
||||
for _, k := range nodes {
|
||||
edge.Target.Nodes = append(edge.Target.Nodes, k)
|
||||
}
|
||||
_spec.Edges.Add = append(_spec.Edges.Add, edge)
|
||||
}
|
||||
if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil {
|
||||
if _, ok := err.(*sqlgraph.NotFoundError); ok {
|
||||
err = &NotFoundError{compositemodelroute.Label}
|
||||
} else if sqlgraph.IsConstraintError(err) {
|
||||
err = &ConstraintError{msg: err.Error(), wrap: err}
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
_u.mutation.done = true
|
||||
return _node, nil
|
||||
}
|
||||
|
||||
// CompositeModelRouteUpdateOne is the builder for updating a single CompositeModelRoute entity.
|
||||
type CompositeModelRouteUpdateOne struct {
|
||||
config
|
||||
fields []string
|
||||
hooks []Hook
|
||||
mutation *CompositeModelRouteMutation
|
||||
}
|
||||
|
||||
// SetUpdatedAt sets the "updated_at" field.
|
||||
func (_u *CompositeModelRouteUpdateOne) SetUpdatedAt(v time.Time) *CompositeModelRouteUpdateOne {
|
||||
_u.mutation.SetUpdatedAt(v)
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetDeletedAt sets the "deleted_at" field.
|
||||
func (_u *CompositeModelRouteUpdateOne) SetDeletedAt(v time.Time) *CompositeModelRouteUpdateOne {
|
||||
_u.mutation.SetDeletedAt(v)
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetNillableDeletedAt sets the "deleted_at" field if the given value is not nil.
|
||||
func (_u *CompositeModelRouteUpdateOne) SetNillableDeletedAt(v *time.Time) *CompositeModelRouteUpdateOne {
|
||||
if v != nil {
|
||||
_u.SetDeletedAt(*v)
|
||||
}
|
||||
return _u
|
||||
}
|
||||
|
||||
// ClearDeletedAt clears the value of the "deleted_at" field.
|
||||
func (_u *CompositeModelRouteUpdateOne) ClearDeletedAt() *CompositeModelRouteUpdateOne {
|
||||
_u.mutation.ClearDeletedAt()
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetGroupID sets the "group_id" field.
|
||||
func (_u *CompositeModelRouteUpdateOne) SetGroupID(v int64) *CompositeModelRouteUpdateOne {
|
||||
_u.mutation.SetGroupID(v)
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetNillableGroupID sets the "group_id" field if the given value is not nil.
|
||||
func (_u *CompositeModelRouteUpdateOne) SetNillableGroupID(v *int64) *CompositeModelRouteUpdateOne {
|
||||
if v != nil {
|
||||
_u.SetGroupID(*v)
|
||||
}
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetPublicModel sets the "public_model" field.
|
||||
func (_u *CompositeModelRouteUpdateOne) SetPublicModel(v string) *CompositeModelRouteUpdateOne {
|
||||
_u.mutation.SetPublicModel(v)
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetNillablePublicModel sets the "public_model" field if the given value is not nil.
|
||||
func (_u *CompositeModelRouteUpdateOne) SetNillablePublicModel(v *string) *CompositeModelRouteUpdateOne {
|
||||
if v != nil {
|
||||
_u.SetPublicModel(*v)
|
||||
}
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetMatchType sets the "match_type" field.
|
||||
func (_u *CompositeModelRouteUpdateOne) SetMatchType(v string) *CompositeModelRouteUpdateOne {
|
||||
_u.mutation.SetMatchType(v)
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetNillableMatchType sets the "match_type" field if the given value is not nil.
|
||||
func (_u *CompositeModelRouteUpdateOne) SetNillableMatchType(v *string) *CompositeModelRouteUpdateOne {
|
||||
if v != nil {
|
||||
_u.SetMatchType(*v)
|
||||
}
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetTargetPlatform sets the "target_platform" field.
|
||||
func (_u *CompositeModelRouteUpdateOne) SetTargetPlatform(v string) *CompositeModelRouteUpdateOne {
|
||||
_u.mutation.SetTargetPlatform(v)
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetNillableTargetPlatform sets the "target_platform" field if the given value is not nil.
|
||||
func (_u *CompositeModelRouteUpdateOne) SetNillableTargetPlatform(v *string) *CompositeModelRouteUpdateOne {
|
||||
if v != nil {
|
||||
_u.SetTargetPlatform(*v)
|
||||
}
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetUpstreamModel sets the "upstream_model" field.
|
||||
func (_u *CompositeModelRouteUpdateOne) SetUpstreamModel(v string) *CompositeModelRouteUpdateOne {
|
||||
_u.mutation.SetUpstreamModel(v)
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetNillableUpstreamModel sets the "upstream_model" field if the given value is not nil.
|
||||
func (_u *CompositeModelRouteUpdateOne) SetNillableUpstreamModel(v *string) *CompositeModelRouteUpdateOne {
|
||||
if v != nil {
|
||||
_u.SetUpstreamModel(*v)
|
||||
}
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetEndpoint sets the "endpoint" field.
|
||||
func (_u *CompositeModelRouteUpdateOne) SetEndpoint(v string) *CompositeModelRouteUpdateOne {
|
||||
_u.mutation.SetEndpoint(v)
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetNillableEndpoint sets the "endpoint" field if the given value is not nil.
|
||||
func (_u *CompositeModelRouteUpdateOne) SetNillableEndpoint(v *string) *CompositeModelRouteUpdateOne {
|
||||
if v != nil {
|
||||
_u.SetEndpoint(*v)
|
||||
}
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetPriority sets the "priority" field.
|
||||
func (_u *CompositeModelRouteUpdateOne) SetPriority(v int) *CompositeModelRouteUpdateOne {
|
||||
_u.mutation.ResetPriority()
|
||||
_u.mutation.SetPriority(v)
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetNillablePriority sets the "priority" field if the given value is not nil.
|
||||
func (_u *CompositeModelRouteUpdateOne) SetNillablePriority(v *int) *CompositeModelRouteUpdateOne {
|
||||
if v != nil {
|
||||
_u.SetPriority(*v)
|
||||
}
|
||||
return _u
|
||||
}
|
||||
|
||||
// AddPriority adds value to the "priority" field.
|
||||
func (_u *CompositeModelRouteUpdateOne) AddPriority(v int) *CompositeModelRouteUpdateOne {
|
||||
_u.mutation.AddPriority(v)
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetEnabled sets the "enabled" field.
|
||||
func (_u *CompositeModelRouteUpdateOne) SetEnabled(v bool) *CompositeModelRouteUpdateOne {
|
||||
_u.mutation.SetEnabled(v)
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetNillableEnabled sets the "enabled" field if the given value is not nil.
|
||||
func (_u *CompositeModelRouteUpdateOne) SetNillableEnabled(v *bool) *CompositeModelRouteUpdateOne {
|
||||
if v != nil {
|
||||
_u.SetEnabled(*v)
|
||||
}
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetNotes sets the "notes" field.
|
||||
func (_u *CompositeModelRouteUpdateOne) SetNotes(v string) *CompositeModelRouteUpdateOne {
|
||||
_u.mutation.SetNotes(v)
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetNillableNotes sets the "notes" field if the given value is not nil.
|
||||
func (_u *CompositeModelRouteUpdateOne) SetNillableNotes(v *string) *CompositeModelRouteUpdateOne {
|
||||
if v != nil {
|
||||
_u.SetNotes(*v)
|
||||
}
|
||||
return _u
|
||||
}
|
||||
|
||||
// ClearNotes clears the value of the "notes" field.
|
||||
func (_u *CompositeModelRouteUpdateOne) ClearNotes() *CompositeModelRouteUpdateOne {
|
||||
_u.mutation.ClearNotes()
|
||||
return _u
|
||||
}
|
||||
|
||||
// SetGroup sets the "group" edge to the Group entity.
|
||||
func (_u *CompositeModelRouteUpdateOne) SetGroup(v *Group) *CompositeModelRouteUpdateOne {
|
||||
return _u.SetGroupID(v.ID)
|
||||
}
|
||||
|
||||
// Mutation returns the CompositeModelRouteMutation object of the builder.
|
||||
func (_u *CompositeModelRouteUpdateOne) Mutation() *CompositeModelRouteMutation {
|
||||
return _u.mutation
|
||||
}
|
||||
|
||||
// ClearGroup clears the "group" edge to the Group entity.
|
||||
func (_u *CompositeModelRouteUpdateOne) ClearGroup() *CompositeModelRouteUpdateOne {
|
||||
_u.mutation.ClearGroup()
|
||||
return _u
|
||||
}
|
||||
|
||||
// Where appends a list predicates to the CompositeModelRouteUpdate builder.
|
||||
func (_u *CompositeModelRouteUpdateOne) Where(ps ...predicate.CompositeModelRoute) *CompositeModelRouteUpdateOne {
|
||||
_u.mutation.Where(ps...)
|
||||
return _u
|
||||
}
|
||||
|
||||
// Select allows selecting one or more fields (columns) of the returned entity.
|
||||
// The default is selecting all fields defined in the entity schema.
|
||||
func (_u *CompositeModelRouteUpdateOne) Select(field string, fields ...string) *CompositeModelRouteUpdateOne {
|
||||
_u.fields = append([]string{field}, fields...)
|
||||
return _u
|
||||
}
|
||||
|
||||
// Save executes the query and returns the updated CompositeModelRoute entity.
|
||||
func (_u *CompositeModelRouteUpdateOne) Save(ctx context.Context) (*CompositeModelRoute, error) {
|
||||
if err := _u.defaults(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks)
|
||||
}
|
||||
|
||||
// SaveX is like Save, but panics if an error occurs.
|
||||
func (_u *CompositeModelRouteUpdateOne) SaveX(ctx context.Context) *CompositeModelRoute {
|
||||
node, err := _u.Save(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
||||
// Exec executes the query on the entity.
|
||||
func (_u *CompositeModelRouteUpdateOne) Exec(ctx context.Context) error {
|
||||
_, err := _u.Save(ctx)
|
||||
return err
|
||||
}
|
||||
|
||||
// ExecX is like Exec, but panics if an error occurs.
|
||||
func (_u *CompositeModelRouteUpdateOne) ExecX(ctx context.Context) {
|
||||
if err := _u.Exec(ctx); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// defaults sets the default values of the builder before save.
|
||||
func (_u *CompositeModelRouteUpdateOne) defaults() error {
|
||||
if _, ok := _u.mutation.UpdatedAt(); !ok {
|
||||
if compositemodelroute.UpdateDefaultUpdatedAt == nil {
|
||||
return fmt.Errorf("ent: uninitialized compositemodelroute.UpdateDefaultUpdatedAt (forgotten import ent/runtime?)")
|
||||
}
|
||||
v := compositemodelroute.UpdateDefaultUpdatedAt()
|
||||
_u.mutation.SetUpdatedAt(v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// check runs all checks and user-defined validators on the builder.
|
||||
func (_u *CompositeModelRouteUpdateOne) check() error {
|
||||
if v, ok := _u.mutation.PublicModel(); ok {
|
||||
if err := compositemodelroute.PublicModelValidator(v); err != nil {
|
||||
return &ValidationError{Name: "public_model", err: fmt.Errorf(`ent: validator failed for field "CompositeModelRoute.public_model": %w`, err)}
|
||||
}
|
||||
}
|
||||
if v, ok := _u.mutation.MatchType(); ok {
|
||||
if err := compositemodelroute.MatchTypeValidator(v); err != nil {
|
||||
return &ValidationError{Name: "match_type", err: fmt.Errorf(`ent: validator failed for field "CompositeModelRoute.match_type": %w`, err)}
|
||||
}
|
||||
}
|
||||
if v, ok := _u.mutation.TargetPlatform(); ok {
|
||||
if err := compositemodelroute.TargetPlatformValidator(v); err != nil {
|
||||
return &ValidationError{Name: "target_platform", err: fmt.Errorf(`ent: validator failed for field "CompositeModelRoute.target_platform": %w`, err)}
|
||||
}
|
||||
}
|
||||
if v, ok := _u.mutation.UpstreamModel(); ok {
|
||||
if err := compositemodelroute.UpstreamModelValidator(v); err != nil {
|
||||
return &ValidationError{Name: "upstream_model", err: fmt.Errorf(`ent: validator failed for field "CompositeModelRoute.upstream_model": %w`, err)}
|
||||
}
|
||||
}
|
||||
if v, ok := _u.mutation.Endpoint(); ok {
|
||||
if err := compositemodelroute.EndpointValidator(v); err != nil {
|
||||
return &ValidationError{Name: "endpoint", err: fmt.Errorf(`ent: validator failed for field "CompositeModelRoute.endpoint": %w`, err)}
|
||||
}
|
||||
}
|
||||
if _u.mutation.GroupCleared() && len(_u.mutation.GroupIDs()) > 0 {
|
||||
return errors.New(`ent: clearing a required unique edge "CompositeModelRoute.group"`)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_u *CompositeModelRouteUpdateOne) sqlSave(ctx context.Context) (_node *CompositeModelRoute, err error) {
|
||||
if err := _u.check(); err != nil {
|
||||
return _node, err
|
||||
}
|
||||
_spec := sqlgraph.NewUpdateSpec(compositemodelroute.Table, compositemodelroute.Columns, sqlgraph.NewFieldSpec(compositemodelroute.FieldID, field.TypeInt64))
|
||||
id, ok := _u.mutation.ID()
|
||||
if !ok {
|
||||
return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "CompositeModelRoute.id" for update`)}
|
||||
}
|
||||
_spec.Node.ID.Value = id
|
||||
if fields := _u.fields; len(fields) > 0 {
|
||||
_spec.Node.Columns = make([]string, 0, len(fields))
|
||||
_spec.Node.Columns = append(_spec.Node.Columns, compositemodelroute.FieldID)
|
||||
for _, f := range fields {
|
||||
if !compositemodelroute.ValidColumn(f) {
|
||||
return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
|
||||
}
|
||||
if f != compositemodelroute.FieldID {
|
||||
_spec.Node.Columns = append(_spec.Node.Columns, f)
|
||||
}
|
||||
}
|
||||
}
|
||||
if ps := _u.mutation.predicates; len(ps) > 0 {
|
||||
_spec.Predicate = func(selector *sql.Selector) {
|
||||
for i := range ps {
|
||||
ps[i](selector)
|
||||
}
|
||||
}
|
||||
}
|
||||
if value, ok := _u.mutation.UpdatedAt(); ok {
|
||||
_spec.SetField(compositemodelroute.FieldUpdatedAt, field.TypeTime, value)
|
||||
}
|
||||
if value, ok := _u.mutation.DeletedAt(); ok {
|
||||
_spec.SetField(compositemodelroute.FieldDeletedAt, field.TypeTime, value)
|
||||
}
|
||||
if _u.mutation.DeletedAtCleared() {
|
||||
_spec.ClearField(compositemodelroute.FieldDeletedAt, field.TypeTime)
|
||||
}
|
||||
if value, ok := _u.mutation.PublicModel(); ok {
|
||||
_spec.SetField(compositemodelroute.FieldPublicModel, field.TypeString, value)
|
||||
}
|
||||
if value, ok := _u.mutation.MatchType(); ok {
|
||||
_spec.SetField(compositemodelroute.FieldMatchType, field.TypeString, value)
|
||||
}
|
||||
if value, ok := _u.mutation.TargetPlatform(); ok {
|
||||
_spec.SetField(compositemodelroute.FieldTargetPlatform, field.TypeString, value)
|
||||
}
|
||||
if value, ok := _u.mutation.UpstreamModel(); ok {
|
||||
_spec.SetField(compositemodelroute.FieldUpstreamModel, field.TypeString, value)
|
||||
}
|
||||
if value, ok := _u.mutation.Endpoint(); ok {
|
||||
_spec.SetField(compositemodelroute.FieldEndpoint, field.TypeString, value)
|
||||
}
|
||||
if value, ok := _u.mutation.Priority(); ok {
|
||||
_spec.SetField(compositemodelroute.FieldPriority, field.TypeInt, value)
|
||||
}
|
||||
if value, ok := _u.mutation.AddedPriority(); ok {
|
||||
_spec.AddField(compositemodelroute.FieldPriority, field.TypeInt, value)
|
||||
}
|
||||
if value, ok := _u.mutation.Enabled(); ok {
|
||||
_spec.SetField(compositemodelroute.FieldEnabled, field.TypeBool, value)
|
||||
}
|
||||
if value, ok := _u.mutation.Notes(); ok {
|
||||
_spec.SetField(compositemodelroute.FieldNotes, field.TypeString, value)
|
||||
}
|
||||
if _u.mutation.NotesCleared() {
|
||||
_spec.ClearField(compositemodelroute.FieldNotes, field.TypeString)
|
||||
}
|
||||
if _u.mutation.GroupCleared() {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.M2O,
|
||||
Inverse: false,
|
||||
Table: compositemodelroute.GroupTable,
|
||||
Columns: []string{compositemodelroute.GroupColumn},
|
||||
Bidi: false,
|
||||
Target: &sqlgraph.EdgeTarget{
|
||||
IDSpec: sqlgraph.NewFieldSpec(group.FieldID, field.TypeInt64),
|
||||
},
|
||||
}
|
||||
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
|
||||
}
|
||||
if nodes := _u.mutation.GroupIDs(); len(nodes) > 0 {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.M2O,
|
||||
Inverse: false,
|
||||
Table: compositemodelroute.GroupTable,
|
||||
Columns: []string{compositemodelroute.GroupColumn},
|
||||
Bidi: false,
|
||||
Target: &sqlgraph.EdgeTarget{
|
||||
IDSpec: sqlgraph.NewFieldSpec(group.FieldID, field.TypeInt64),
|
||||
},
|
||||
}
|
||||
for _, k := range nodes {
|
||||
edge.Target.Nodes = append(edge.Target.Nodes, k)
|
||||
}
|
||||
_spec.Edges.Add = append(_spec.Edges.Add, edge)
|
||||
}
|
||||
_node = &CompositeModelRoute{config: _u.config}
|
||||
_spec.Assign = _node.assignValues
|
||||
_spec.ScanValues = _node.scanValues
|
||||
if err = sqlgraph.UpdateNode(ctx, _u.driver, _spec); err != nil {
|
||||
if _, ok := err.(*sqlgraph.NotFoundError); ok {
|
||||
err = &NotFoundError{compositemodelroute.Label}
|
||||
} else if sqlgraph.IsConstraintError(err) {
|
||||
err = &ConstraintError{msg: err.Error(), wrap: err}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
_u.mutation.done = true
|
||||
return _node, nil
|
||||
}
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
"github.com/Wei-Shaw/sub2api/ent/channelmonitordailyrollup"
|
||||
"github.com/Wei-Shaw/sub2api/ent/channelmonitorhistory"
|
||||
"github.com/Wei-Shaw/sub2api/ent/channelmonitorrequesttemplate"
|
||||
"github.com/Wei-Shaw/sub2api/ent/compositemodelroute"
|
||||
"github.com/Wei-Shaw/sub2api/ent/errorpassthroughrule"
|
||||
"github.com/Wei-Shaw/sub2api/ent/group"
|
||||
"github.com/Wei-Shaw/sub2api/ent/idempotencyrecord"
|
||||
@@ -124,6 +125,7 @@ func checkColumn(t, c string) error {
|
||||
channelmonitordailyrollup.Table: channelmonitordailyrollup.ValidColumn,
|
||||
channelmonitorhistory.Table: channelmonitorhistory.ValidColumn,
|
||||
channelmonitorrequesttemplate.Table: channelmonitorrequesttemplate.ValidColumn,
|
||||
compositemodelroute.Table: compositemodelroute.ValidColumn,
|
||||
errorpassthroughrule.Table: errorpassthroughrule.ValidColumn,
|
||||
group.Table: group.ValidColumn,
|
||||
idempotencyrecord.Table: idempotencyrecord.ValidColumn,
|
||||
|
||||
@@ -177,6 +177,18 @@ func (f ChannelMonitorRequestTemplateFunc) Mutate(ctx context.Context, m ent.Mut
|
||||
return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.ChannelMonitorRequestTemplateMutation", m)
|
||||
}
|
||||
|
||||
// The CompositeModelRouteFunc type is an adapter to allow the use of ordinary
|
||||
// function as CompositeModelRoute mutator.
|
||||
type CompositeModelRouteFunc func(context.Context, *ent.CompositeModelRouteMutation) (ent.Value, error)
|
||||
|
||||
// Mutate calls f(ctx, m).
|
||||
func (f CompositeModelRouteFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) {
|
||||
if mv, ok := m.(*ent.CompositeModelRouteMutation); ok {
|
||||
return f(ctx, mv)
|
||||
}
|
||||
return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.CompositeModelRouteMutation", m)
|
||||
}
|
||||
|
||||
// The ErrorPassthroughRuleFunc type is an adapter to allow the use of ordinary
|
||||
// function as ErrorPassthroughRule mutator.
|
||||
type ErrorPassthroughRuleFunc func(context.Context, *ent.ErrorPassthroughRuleMutation) (ent.Value, error)
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"github.com/Wei-Shaw/sub2api/ent/channelmonitordailyrollup"
|
||||
"github.com/Wei-Shaw/sub2api/ent/channelmonitorhistory"
|
||||
"github.com/Wei-Shaw/sub2api/ent/channelmonitorrequesttemplate"
|
||||
"github.com/Wei-Shaw/sub2api/ent/compositemodelroute"
|
||||
"github.com/Wei-Shaw/sub2api/ent/errorpassthroughrule"
|
||||
"github.com/Wei-Shaw/sub2api/ent/group"
|
||||
"github.com/Wei-Shaw/sub2api/ent/idempotencyrecord"
|
||||
@@ -483,6 +484,33 @@ func (f TraverseChannelMonitorRequestTemplate) Traverse(ctx context.Context, q e
|
||||
return fmt.Errorf("unexpected query type %T. expect *ent.ChannelMonitorRequestTemplateQuery", q)
|
||||
}
|
||||
|
||||
// The CompositeModelRouteFunc type is an adapter to allow the use of ordinary function as a Querier.
|
||||
type CompositeModelRouteFunc func(context.Context, *ent.CompositeModelRouteQuery) (ent.Value, error)
|
||||
|
||||
// Query calls f(ctx, q).
|
||||
func (f CompositeModelRouteFunc) Query(ctx context.Context, q ent.Query) (ent.Value, error) {
|
||||
if q, ok := q.(*ent.CompositeModelRouteQuery); ok {
|
||||
return f(ctx, q)
|
||||
}
|
||||
return nil, fmt.Errorf("unexpected query type %T. expect *ent.CompositeModelRouteQuery", q)
|
||||
}
|
||||
|
||||
// The TraverseCompositeModelRoute type is an adapter to allow the use of ordinary function as Traverser.
|
||||
type TraverseCompositeModelRoute func(context.Context, *ent.CompositeModelRouteQuery) error
|
||||
|
||||
// Intercept is a dummy implementation of Intercept that returns the next Querier in the pipeline.
|
||||
func (f TraverseCompositeModelRoute) Intercept(next ent.Querier) ent.Querier {
|
||||
return next
|
||||
}
|
||||
|
||||
// Traverse calls f(ctx, q).
|
||||
func (f TraverseCompositeModelRoute) Traverse(ctx context.Context, q ent.Query) error {
|
||||
if q, ok := q.(*ent.CompositeModelRouteQuery); ok {
|
||||
return f(ctx, q)
|
||||
}
|
||||
return fmt.Errorf("unexpected query type %T. expect *ent.CompositeModelRouteQuery", q)
|
||||
}
|
||||
|
||||
// The ErrorPassthroughRuleFunc type is an adapter to allow the use of ordinary function as a Querier.
|
||||
type ErrorPassthroughRuleFunc func(context.Context, *ent.ErrorPassthroughRuleQuery) (ent.Value, error)
|
||||
|
||||
@@ -1162,6 +1190,8 @@ func NewQuery(q ent.Query) (Query, error) {
|
||||
return &query[*ent.ChannelMonitorHistoryQuery, predicate.ChannelMonitorHistory, channelmonitorhistory.OrderOption]{typ: ent.TypeChannelMonitorHistory, tq: q}, nil
|
||||
case *ent.ChannelMonitorRequestTemplateQuery:
|
||||
return &query[*ent.ChannelMonitorRequestTemplateQuery, predicate.ChannelMonitorRequestTemplate, channelmonitorrequesttemplate.OrderOption]{typ: ent.TypeChannelMonitorRequestTemplate, tq: q}, nil
|
||||
case *ent.CompositeModelRouteQuery:
|
||||
return &query[*ent.CompositeModelRouteQuery, predicate.CompositeModelRoute, compositemodelroute.OrderOption]{typ: ent.TypeCompositeModelRoute, tq: q}, nil
|
||||
case *ent.ErrorPassthroughRuleQuery:
|
||||
return &query[*ent.ErrorPassthroughRuleQuery, predicate.ErrorPassthroughRule, errorpassthroughrule.OrderOption]{typ: ent.TypeErrorPassthroughRule, tq: q}, nil
|
||||
case *ent.GroupQuery:
|
||||
|
||||
@@ -793,6 +793,68 @@ var (
|
||||
},
|
||||
},
|
||||
}
|
||||
// CompositeModelRoutesColumns holds the columns for the "composite_model_routes" table.
|
||||
CompositeModelRoutesColumns = []*schema.Column{
|
||||
{Name: "id", Type: field.TypeInt64, Increment: true},
|
||||
{Name: "created_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
||||
{Name: "updated_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
||||
{Name: "deleted_at", Type: field.TypeTime, Nullable: true, SchemaType: map[string]string{"postgres": "timestamptz"}},
|
||||
{Name: "public_model", Type: field.TypeString, Size: 200},
|
||||
{Name: "match_type", Type: field.TypeString, Size: 20, Default: "exact"},
|
||||
{Name: "target_platform", Type: field.TypeString, Size: 50, Default: "openai"},
|
||||
{Name: "upstream_model", Type: field.TypeString, Size: 200, Default: ""},
|
||||
{Name: "endpoint", Type: field.TypeString, Size: 50, Default: "any"},
|
||||
{Name: "priority", Type: field.TypeInt, Default: 100},
|
||||
{Name: "enabled", Type: field.TypeBool, Default: true},
|
||||
{Name: "notes", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"postgres": "text"}},
|
||||
{Name: "group_id", Type: field.TypeInt64},
|
||||
}
|
||||
// CompositeModelRoutesTable holds the schema information for the "composite_model_routes" table.
|
||||
CompositeModelRoutesTable = &schema.Table{
|
||||
Name: "composite_model_routes",
|
||||
Columns: CompositeModelRoutesColumns,
|
||||
PrimaryKey: []*schema.Column{CompositeModelRoutesColumns[0]},
|
||||
ForeignKeys: []*schema.ForeignKey{
|
||||
{
|
||||
Symbol: "composite_model_routes_groups_group",
|
||||
Columns: []*schema.Column{CompositeModelRoutesColumns[12]},
|
||||
RefColumns: []*schema.Column{GroupsColumns[0]},
|
||||
OnDelete: schema.NoAction,
|
||||
},
|
||||
},
|
||||
Indexes: []*schema.Index{
|
||||
{
|
||||
Name: "compositemodelroute_group_id",
|
||||
Unique: false,
|
||||
Columns: []*schema.Column{CompositeModelRoutesColumns[12]},
|
||||
},
|
||||
{
|
||||
Name: "compositemodelroute_group_id_enabled",
|
||||
Unique: false,
|
||||
Columns: []*schema.Column{CompositeModelRoutesColumns[12], CompositeModelRoutesColumns[10]},
|
||||
},
|
||||
{
|
||||
Name: "compositemodelroute_group_id_endpoint",
|
||||
Unique: false,
|
||||
Columns: []*schema.Column{CompositeModelRoutesColumns[12], CompositeModelRoutesColumns[8]},
|
||||
},
|
||||
{
|
||||
Name: "compositemodelroute_group_id_target_platform",
|
||||
Unique: false,
|
||||
Columns: []*schema.Column{CompositeModelRoutesColumns[12], CompositeModelRoutesColumns[6]},
|
||||
},
|
||||
{
|
||||
Name: "compositemodelroute_deleted_at",
|
||||
Unique: false,
|
||||
Columns: []*schema.Column{CompositeModelRoutesColumns[3]},
|
||||
},
|
||||
{
|
||||
Name: "compositemodelroute_priority",
|
||||
Unique: false,
|
||||
Columns: []*schema.Column{CompositeModelRoutesColumns[9]},
|
||||
},
|
||||
},
|
||||
}
|
||||
// ErrorPassthroughRulesColumns holds the columns for the "error_passthrough_rules" table.
|
||||
ErrorPassthroughRulesColumns = []*schema.Column{
|
||||
{Name: "id", Type: field.TypeInt64, Increment: true},
|
||||
@@ -2014,6 +2076,7 @@ var (
|
||||
ChannelMonitorDailyRollupsTable,
|
||||
ChannelMonitorHistoriesTable,
|
||||
ChannelMonitorRequestTemplatesTable,
|
||||
CompositeModelRoutesTable,
|
||||
ErrorPassthroughRulesTable,
|
||||
GroupsTable,
|
||||
IdempotencyRecordsTable,
|
||||
@@ -2097,6 +2160,10 @@ func init() {
|
||||
ChannelMonitorRequestTemplatesTable.Annotation = &entsql.Annotation{
|
||||
Table: "channel_monitor_request_templates",
|
||||
}
|
||||
CompositeModelRoutesTable.ForeignKeys[0].RefTable = GroupsTable
|
||||
CompositeModelRoutesTable.Annotation = &entsql.Annotation{
|
||||
Table: "composite_model_routes",
|
||||
}
|
||||
ErrorPassthroughRulesTable.Annotation = &entsql.Annotation{
|
||||
Table: "error_passthrough_rules",
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -48,6 +48,9 @@ type ChannelMonitorHistory func(*sql.Selector)
|
||||
// ChannelMonitorRequestTemplate is the predicate function for channelmonitorrequesttemplate builders.
|
||||
type ChannelMonitorRequestTemplate func(*sql.Selector)
|
||||
|
||||
// CompositeModelRoute is the predicate function for compositemodelroute builders.
|
||||
type CompositeModelRoute func(*sql.Selector)
|
||||
|
||||
// ErrorPassthroughRule is the predicate function for errorpassthroughrule builders.
|
||||
type ErrorPassthroughRule func(*sql.Selector)
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"github.com/Wei-Shaw/sub2api/ent/channelmonitordailyrollup"
|
||||
"github.com/Wei-Shaw/sub2api/ent/channelmonitorhistory"
|
||||
"github.com/Wei-Shaw/sub2api/ent/channelmonitorrequesttemplate"
|
||||
"github.com/Wei-Shaw/sub2api/ent/compositemodelroute"
|
||||
"github.com/Wei-Shaw/sub2api/ent/errorpassthroughrule"
|
||||
"github.com/Wei-Shaw/sub2api/ent/group"
|
||||
"github.com/Wei-Shaw/sub2api/ent/idempotencyrecord"
|
||||
@@ -865,6 +866,75 @@ func init() {
|
||||
channelmonitorrequesttemplate.DefaultBodyOverrideMode = channelmonitorrequesttemplateDescBodyOverrideMode.Default.(string)
|
||||
// channelmonitorrequesttemplate.BodyOverrideModeValidator is a validator for the "body_override_mode" field. It is called by the builders before save.
|
||||
channelmonitorrequesttemplate.BodyOverrideModeValidator = channelmonitorrequesttemplateDescBodyOverrideMode.Validators[0].(func(string) error)
|
||||
compositemodelrouteMixin := schema.CompositeModelRoute{}.Mixin()
|
||||
compositemodelrouteMixinHooks1 := compositemodelrouteMixin[1].Hooks()
|
||||
compositemodelroute.Hooks[0] = compositemodelrouteMixinHooks1[0]
|
||||
compositemodelrouteMixinInters1 := compositemodelrouteMixin[1].Interceptors()
|
||||
compositemodelroute.Interceptors[0] = compositemodelrouteMixinInters1[0]
|
||||
compositemodelrouteMixinFields0 := compositemodelrouteMixin[0].Fields()
|
||||
_ = compositemodelrouteMixinFields0
|
||||
compositemodelrouteFields := schema.CompositeModelRoute{}.Fields()
|
||||
_ = compositemodelrouteFields
|
||||
// compositemodelrouteDescCreatedAt is the schema descriptor for created_at field.
|
||||
compositemodelrouteDescCreatedAt := compositemodelrouteMixinFields0[0].Descriptor()
|
||||
// compositemodelroute.DefaultCreatedAt holds the default value on creation for the created_at field.
|
||||
compositemodelroute.DefaultCreatedAt = compositemodelrouteDescCreatedAt.Default.(func() time.Time)
|
||||
// compositemodelrouteDescUpdatedAt is the schema descriptor for updated_at field.
|
||||
compositemodelrouteDescUpdatedAt := compositemodelrouteMixinFields0[1].Descriptor()
|
||||
// compositemodelroute.DefaultUpdatedAt holds the default value on creation for the updated_at field.
|
||||
compositemodelroute.DefaultUpdatedAt = compositemodelrouteDescUpdatedAt.Default.(func() time.Time)
|
||||
// compositemodelroute.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field.
|
||||
compositemodelroute.UpdateDefaultUpdatedAt = compositemodelrouteDescUpdatedAt.UpdateDefault.(func() time.Time)
|
||||
// compositemodelrouteDescPublicModel is the schema descriptor for public_model field.
|
||||
compositemodelrouteDescPublicModel := compositemodelrouteFields[1].Descriptor()
|
||||
// compositemodelroute.PublicModelValidator is a validator for the "public_model" field. It is called by the builders before save.
|
||||
compositemodelroute.PublicModelValidator = func() func(string) error {
|
||||
validators := compositemodelrouteDescPublicModel.Validators
|
||||
fns := [...]func(string) error{
|
||||
validators[0].(func(string) error),
|
||||
validators[1].(func(string) error),
|
||||
}
|
||||
return func(public_model string) error {
|
||||
for _, fn := range fns {
|
||||
if err := fn(public_model); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}()
|
||||
// compositemodelrouteDescMatchType is the schema descriptor for match_type field.
|
||||
compositemodelrouteDescMatchType := compositemodelrouteFields[2].Descriptor()
|
||||
// compositemodelroute.DefaultMatchType holds the default value on creation for the match_type field.
|
||||
compositemodelroute.DefaultMatchType = compositemodelrouteDescMatchType.Default.(string)
|
||||
// compositemodelroute.MatchTypeValidator is a validator for the "match_type" field. It is called by the builders before save.
|
||||
compositemodelroute.MatchTypeValidator = compositemodelrouteDescMatchType.Validators[0].(func(string) error)
|
||||
// compositemodelrouteDescTargetPlatform is the schema descriptor for target_platform field.
|
||||
compositemodelrouteDescTargetPlatform := compositemodelrouteFields[3].Descriptor()
|
||||
// compositemodelroute.DefaultTargetPlatform holds the default value on creation for the target_platform field.
|
||||
compositemodelroute.DefaultTargetPlatform = compositemodelrouteDescTargetPlatform.Default.(string)
|
||||
// compositemodelroute.TargetPlatformValidator is a validator for the "target_platform" field. It is called by the builders before save.
|
||||
compositemodelroute.TargetPlatformValidator = compositemodelrouteDescTargetPlatform.Validators[0].(func(string) error)
|
||||
// compositemodelrouteDescUpstreamModel is the schema descriptor for upstream_model field.
|
||||
compositemodelrouteDescUpstreamModel := compositemodelrouteFields[4].Descriptor()
|
||||
// compositemodelroute.DefaultUpstreamModel holds the default value on creation for the upstream_model field.
|
||||
compositemodelroute.DefaultUpstreamModel = compositemodelrouteDescUpstreamModel.Default.(string)
|
||||
// compositemodelroute.UpstreamModelValidator is a validator for the "upstream_model" field. It is called by the builders before save.
|
||||
compositemodelroute.UpstreamModelValidator = compositemodelrouteDescUpstreamModel.Validators[0].(func(string) error)
|
||||
// compositemodelrouteDescEndpoint is the schema descriptor for endpoint field.
|
||||
compositemodelrouteDescEndpoint := compositemodelrouteFields[5].Descriptor()
|
||||
// compositemodelroute.DefaultEndpoint holds the default value on creation for the endpoint field.
|
||||
compositemodelroute.DefaultEndpoint = compositemodelrouteDescEndpoint.Default.(string)
|
||||
// compositemodelroute.EndpointValidator is a validator for the "endpoint" field. It is called by the builders before save.
|
||||
compositemodelroute.EndpointValidator = compositemodelrouteDescEndpoint.Validators[0].(func(string) error)
|
||||
// compositemodelrouteDescPriority is the schema descriptor for priority field.
|
||||
compositemodelrouteDescPriority := compositemodelrouteFields[6].Descriptor()
|
||||
// compositemodelroute.DefaultPriority holds the default value on creation for the priority field.
|
||||
compositemodelroute.DefaultPriority = compositemodelrouteDescPriority.Default.(int)
|
||||
// compositemodelrouteDescEnabled is the schema descriptor for enabled field.
|
||||
compositemodelrouteDescEnabled := compositemodelrouteFields[7].Descriptor()
|
||||
// compositemodelroute.DefaultEnabled holds the default value on creation for the enabled field.
|
||||
compositemodelroute.DefaultEnabled = compositemodelrouteDescEnabled.Default.(bool)
|
||||
errorpassthroughruleMixin := schema.ErrorPassthroughRule{}.Mixin()
|
||||
errorpassthroughruleMixinFields0 := errorpassthroughruleMixin[0].Fields()
|
||||
_ = errorpassthroughruleMixinFields0
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
package schema
|
||||
|
||||
import (
|
||||
"github.com/Wei-Shaw/sub2api/ent/schema/mixins"
|
||||
"github.com/Wei-Shaw/sub2api/internal/domain"
|
||||
|
||||
"entgo.io/ent"
|
||||
"entgo.io/ent/dialect"
|
||||
"entgo.io/ent/dialect/entsql"
|
||||
"entgo.io/ent/schema"
|
||||
"entgo.io/ent/schema/edge"
|
||||
"entgo.io/ent/schema/field"
|
||||
"entgo.io/ent/schema/index"
|
||||
)
|
||||
|
||||
// CompositeModelRoute holds model routing aliases for composite groups.
|
||||
type CompositeModelRoute struct {
|
||||
ent.Schema
|
||||
}
|
||||
|
||||
func (CompositeModelRoute) Annotations() []schema.Annotation {
|
||||
return []schema.Annotation{
|
||||
entsql.Annotation{Table: "composite_model_routes"},
|
||||
}
|
||||
}
|
||||
|
||||
func (CompositeModelRoute) Mixin() []ent.Mixin {
|
||||
return []ent.Mixin{
|
||||
mixins.TimeMixin{},
|
||||
mixins.SoftDeleteMixin{},
|
||||
}
|
||||
}
|
||||
|
||||
func (CompositeModelRoute) Fields() []ent.Field {
|
||||
return []ent.Field{
|
||||
field.Int64("group_id"),
|
||||
field.String("public_model").
|
||||
MaxLen(200).
|
||||
NotEmpty().
|
||||
Comment("Client-facing model identifier or prefix."),
|
||||
field.String("match_type").
|
||||
MaxLen(20).
|
||||
Default("exact").
|
||||
Comment("exact or prefix."),
|
||||
field.String("target_platform").
|
||||
MaxLen(50).
|
||||
Default(domain.PlatformOpenAI).
|
||||
Comment("Concrete provider platform."),
|
||||
field.String("upstream_model").
|
||||
MaxLen(200).
|
||||
Default("").
|
||||
Comment("Provider model identifier; empty means public_model."),
|
||||
field.String("endpoint").
|
||||
MaxLen(50).
|
||||
Default("any").
|
||||
Comment("Endpoint scope such as any, messages, responses, chat_completions."),
|
||||
field.Int("priority").
|
||||
Default(100).
|
||||
Comment("Lower values win within the same match strength."),
|
||||
field.Bool("enabled").
|
||||
Default(true),
|
||||
field.String("notes").
|
||||
Optional().
|
||||
Nillable().
|
||||
SchemaType(map[string]string{dialect.Postgres: "text"}),
|
||||
}
|
||||
}
|
||||
|
||||
func (CompositeModelRoute) Edges() []ent.Edge {
|
||||
return []ent.Edge{
|
||||
edge.To("group", Group.Type).
|
||||
Unique().
|
||||
Required().
|
||||
Field("group_id"),
|
||||
}
|
||||
}
|
||||
|
||||
func (CompositeModelRoute) Indexes() []ent.Index {
|
||||
return []ent.Index{
|
||||
index.Fields("group_id"),
|
||||
index.Fields("group_id", "enabled"),
|
||||
index.Fields("group_id", "endpoint"),
|
||||
index.Fields("group_id", "target_platform"),
|
||||
index.Fields("deleted_at"),
|
||||
index.Fields("priority"),
|
||||
}
|
||||
}
|
||||
@@ -42,6 +42,8 @@ type Tx struct {
|
||||
ChannelMonitorHistory *ChannelMonitorHistoryClient
|
||||
// ChannelMonitorRequestTemplate is the client for interacting with the ChannelMonitorRequestTemplate builders.
|
||||
ChannelMonitorRequestTemplate *ChannelMonitorRequestTemplateClient
|
||||
// CompositeModelRoute is the client for interacting with the CompositeModelRoute builders.
|
||||
CompositeModelRoute *CompositeModelRouteClient
|
||||
// ErrorPassthroughRule is the client for interacting with the ErrorPassthroughRule builders.
|
||||
ErrorPassthroughRule *ErrorPassthroughRuleClient
|
||||
// Group is the client for interacting with the Group builders.
|
||||
@@ -235,6 +237,7 @@ func (tx *Tx) init() {
|
||||
tx.ChannelMonitorDailyRollup = NewChannelMonitorDailyRollupClient(tx.config)
|
||||
tx.ChannelMonitorHistory = NewChannelMonitorHistoryClient(tx.config)
|
||||
tx.ChannelMonitorRequestTemplate = NewChannelMonitorRequestTemplateClient(tx.config)
|
||||
tx.CompositeModelRoute = NewCompositeModelRouteClient(tx.config)
|
||||
tx.ErrorPassthroughRule = NewErrorPassthroughRuleClient(tx.config)
|
||||
tx.Group = NewGroupClient(tx.config)
|
||||
tx.IdempotencyRecord = NewIdempotencyRecordClient(tx.config)
|
||||
|
||||
@@ -34,6 +34,11 @@ func setupAdminRouter() (*gin.Engine, *stubAdminService) {
|
||||
router.GET("/api/v1/admin/groups", groupHandler.List)
|
||||
router.GET("/api/v1/admin/groups/all", groupHandler.GetAll)
|
||||
router.GET("/api/v1/admin/groups/:id/models-list-candidates", groupHandler.GetModelsListCandidates)
|
||||
router.GET("/api/v1/admin/groups/:id/composite-routes", groupHandler.ListCompositeRoutes)
|
||||
router.POST("/api/v1/admin/groups/:id/composite-routes", groupHandler.CreateCompositeRoute)
|
||||
router.POST("/api/v1/admin/groups/:id/composite-routes/preview", groupHandler.PreviewCompositeRoute)
|
||||
router.PUT("/api/v1/admin/groups/:id/composite-routes/:route_id", groupHandler.UpdateCompositeRoute)
|
||||
router.DELETE("/api/v1/admin/groups/:id/composite-routes/:route_id", groupHandler.DeleteCompositeRoute)
|
||||
router.GET("/api/v1/admin/groups/:id", groupHandler.GetByID)
|
||||
router.POST("/api/v1/admin/groups", groupHandler.Create)
|
||||
router.PUT("/api/v1/admin/groups/:id", groupHandler.Update)
|
||||
@@ -184,7 +189,54 @@ func TestGroupHandlerEndpoints(t *testing.T) {
|
||||
require.Equal(t, http.StatusOK, rec.Code)
|
||||
require.Contains(t, rec.Body.String(), "gpt-5.5")
|
||||
|
||||
body, _ := json.Marshal(map[string]any{"name": "new", "platform": "anthropic", "subscription_type": "standard"})
|
||||
rec = httptest.NewRecorder()
|
||||
req = httptest.NewRequest(http.MethodGet, "/api/v1/admin/groups/2/composite-routes", nil)
|
||||
router.ServeHTTP(rec, req)
|
||||
require.Equal(t, http.StatusOK, rec.Code)
|
||||
require.Contains(t, rec.Body.String(), "openrouter/gpt-5")
|
||||
|
||||
body, _ := json.Marshal(map[string]any{
|
||||
"public_model": "openrouter/gpt-5",
|
||||
"match_type": "exact",
|
||||
"target_platform": "openai",
|
||||
"upstream_model": "gpt-5",
|
||||
"endpoint": "chat_completions",
|
||||
"enabled": true,
|
||||
})
|
||||
rec = httptest.NewRecorder()
|
||||
req = httptest.NewRequest(http.MethodPost, "/api/v1/admin/groups/2/composite-routes", bytes.NewReader(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
router.ServeHTTP(rec, req)
|
||||
require.Equal(t, http.StatusCreated, rec.Code)
|
||||
require.Contains(t, rec.Body.String(), "gpt-5")
|
||||
|
||||
body, _ = json.Marshal(map[string]any{
|
||||
"public_model": "openrouter/gpt-5",
|
||||
"target_platform": "openai",
|
||||
"upstream_model": "gpt-5",
|
||||
"endpoint": "responses",
|
||||
"enabled": true,
|
||||
})
|
||||
rec = httptest.NewRecorder()
|
||||
req = httptest.NewRequest(http.MethodPut, "/api/v1/admin/groups/2/composite-routes/1", bytes.NewReader(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
router.ServeHTTP(rec, req)
|
||||
require.Equal(t, http.StatusOK, rec.Code)
|
||||
|
||||
body, _ = json.Marshal(map[string]any{"model": "gpt-5", "endpoint": "responses"})
|
||||
rec = httptest.NewRecorder()
|
||||
req = httptest.NewRequest(http.MethodPost, "/api/v1/admin/groups/2/composite-routes/preview", bytes.NewReader(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
router.ServeHTTP(rec, req)
|
||||
require.Equal(t, http.StatusOK, rec.Code)
|
||||
require.Contains(t, rec.Body.String(), `"source":"detector"`)
|
||||
|
||||
rec = httptest.NewRecorder()
|
||||
req = httptest.NewRequest(http.MethodDelete, "/api/v1/admin/groups/2/composite-routes/1", nil)
|
||||
router.ServeHTTP(rec, req)
|
||||
require.Equal(t, http.StatusOK, rec.Code)
|
||||
|
||||
body, _ = json.Marshal(map[string]any{"name": "new", "platform": "anthropic", "subscription_type": "standard"})
|
||||
rec = httptest.NewRecorder()
|
||||
req = httptest.NewRequest(http.MethodPost, "/api/v1/admin/groups", bytes.NewReader(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
@@ -293,6 +293,64 @@ func (s *stubAdminService) GetGroupModelsListCandidates(ctx context.Context, id
|
||||
return []string{"claude-sonnet-4-6"}, nil
|
||||
}
|
||||
|
||||
func (s *stubAdminService) ListCompositeRoutes(ctx context.Context, groupID int64) ([]service.CompositeModelRoute, error) {
|
||||
return []service.CompositeModelRoute{
|
||||
{
|
||||
ID: 1,
|
||||
GroupID: groupID,
|
||||
PublicModel: "openrouter/gpt-5",
|
||||
MatchType: service.CompositeRouteMatchExact,
|
||||
TargetPlatform: service.PlatformOpenAI,
|
||||
UpstreamModel: "gpt-5",
|
||||
Endpoint: service.CompositeRouteEndpointAny,
|
||||
Priority: 100,
|
||||
Enabled: true,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *stubAdminService) CreateCompositeRoute(ctx context.Context, groupID int64, input service.CompositeRouteInput) (*service.CompositeModelRoute, error) {
|
||||
return &service.CompositeModelRoute{
|
||||
ID: 1,
|
||||
GroupID: groupID,
|
||||
PublicModel: input.PublicModel,
|
||||
MatchType: input.MatchType,
|
||||
TargetPlatform: input.TargetPlatform,
|
||||
UpstreamModel: input.UpstreamModel,
|
||||
Endpoint: input.Endpoint,
|
||||
Priority: input.Priority,
|
||||
Enabled: input.Enabled,
|
||||
Notes: input.Notes,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *stubAdminService) UpdateCompositeRoute(ctx context.Context, groupID, routeID int64, input service.CompositeRouteInput) (*service.CompositeModelRoute, error) {
|
||||
return &service.CompositeModelRoute{
|
||||
ID: routeID,
|
||||
GroupID: groupID,
|
||||
PublicModel: input.PublicModel,
|
||||
MatchType: input.MatchType,
|
||||
TargetPlatform: input.TargetPlatform,
|
||||
UpstreamModel: input.UpstreamModel,
|
||||
Endpoint: input.Endpoint,
|
||||
Priority: input.Priority,
|
||||
Enabled: input.Enabled,
|
||||
Notes: input.Notes,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *stubAdminService) DeleteCompositeRoute(ctx context.Context, groupID, routeID int64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *stubAdminService) PreviewCompositeRoute(ctx context.Context, groupID int64, input service.CompositeRoutePreviewRequest) (*service.CompositeRouteDecision, error) {
|
||||
decision, err := service.NewCompositeRouteResolver(nil).Resolve(ctx, groupID, input.Model, input.Endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &decision, nil
|
||||
}
|
||||
|
||||
func (s *stubAdminService) CreateGroup(ctx context.Context, input *service.CreateGroupInput) (*service.Group, error) {
|
||||
group := service.Group{ID: 200, Name: input.Name, Status: service.StatusActive}
|
||||
return &group, nil
|
||||
|
||||
@@ -198,6 +198,22 @@ type UpdateGroupRequest struct {
|
||||
CopyAccountsFromGroupIDs []int64 `json:"copy_accounts_from_group_ids"`
|
||||
}
|
||||
|
||||
type CompositeRouteRequest struct {
|
||||
PublicModel string `json:"public_model" binding:"required"`
|
||||
MatchType string `json:"match_type" binding:"omitempty,oneof=exact prefix"`
|
||||
TargetPlatform string `json:"target_platform" binding:"required,oneof=anthropic openai gemini antigravity grok"`
|
||||
UpstreamModel string `json:"upstream_model"`
|
||||
Endpoint string `json:"endpoint" binding:"omitempty,oneof=any messages count_tokens responses chat_completions embeddings images gemini"`
|
||||
Priority int `json:"priority"`
|
||||
Enabled *bool `json:"enabled"`
|
||||
Notes string `json:"notes"`
|
||||
}
|
||||
|
||||
type CompositeRoutePreviewRequest struct {
|
||||
Model string `json:"model" binding:"required"`
|
||||
Endpoint string `json:"endpoint" binding:"omitempty,oneof=any messages count_tokens responses chat_completions embeddings images gemini"`
|
||||
}
|
||||
|
||||
// List handles listing all groups with pagination
|
||||
// GET /api/v1/admin/groups
|
||||
func (h *GroupHandler) List(c *gin.Context) {
|
||||
@@ -233,6 +249,133 @@ func (h *GroupHandler) List(c *gin.Context) {
|
||||
response.Paginated(c, outGroups, total, page, pageSize)
|
||||
}
|
||||
|
||||
// ListCompositeRoutes handles listing composite model routes for one group.
|
||||
// GET /api/v1/admin/groups/:id/composite-routes
|
||||
func (h *GroupHandler) ListCompositeRoutes(c *gin.Context) {
|
||||
groupID, ok := parsePositiveIDParam(c, "id")
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
routes, err := h.adminService.ListCompositeRoutes(c.Request.Context(), groupID)
|
||||
if err != nil {
|
||||
response.ErrorFrom(c, err)
|
||||
return
|
||||
}
|
||||
response.Success(c, routes)
|
||||
}
|
||||
|
||||
// CreateCompositeRoute handles creating one composite model route.
|
||||
// POST /api/v1/admin/groups/:id/composite-routes
|
||||
func (h *GroupHandler) CreateCompositeRoute(c *gin.Context) {
|
||||
groupID, ok := parsePositiveIDParam(c, "id")
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
var req CompositeRouteRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "Invalid request body: "+err.Error())
|
||||
return
|
||||
}
|
||||
route, err := h.adminService.CreateCompositeRoute(c.Request.Context(), groupID, compositeRouteRequestToInput(req, true))
|
||||
if err != nil {
|
||||
response.ErrorFrom(c, err)
|
||||
return
|
||||
}
|
||||
response.Created(c, route)
|
||||
}
|
||||
|
||||
// UpdateCompositeRoute handles replacing one composite model route.
|
||||
// PUT /api/v1/admin/groups/:id/composite-routes/:route_id
|
||||
func (h *GroupHandler) UpdateCompositeRoute(c *gin.Context) {
|
||||
groupID, ok := parsePositiveIDParam(c, "id")
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
routeID, ok := parsePositiveIDParam(c, "route_id")
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
var req CompositeRouteRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "Invalid request body: "+err.Error())
|
||||
return
|
||||
}
|
||||
route, err := h.adminService.UpdateCompositeRoute(c.Request.Context(), groupID, routeID, compositeRouteRequestToInput(req, true))
|
||||
if err != nil {
|
||||
response.ErrorFrom(c, err)
|
||||
return
|
||||
}
|
||||
response.Success(c, route)
|
||||
}
|
||||
|
||||
// DeleteCompositeRoute handles deleting one composite model route.
|
||||
// DELETE /api/v1/admin/groups/:id/composite-routes/:route_id
|
||||
func (h *GroupHandler) DeleteCompositeRoute(c *gin.Context) {
|
||||
groupID, ok := parsePositiveIDParam(c, "id")
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
routeID, ok := parsePositiveIDParam(c, "route_id")
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if err := h.adminService.DeleteCompositeRoute(c.Request.Context(), groupID, routeID); err != nil {
|
||||
response.ErrorFrom(c, err)
|
||||
return
|
||||
}
|
||||
response.Success(c, gin.H{"message": "Composite route deleted"})
|
||||
}
|
||||
|
||||
// PreviewCompositeRoute resolves a model without mutating routes.
|
||||
// POST /api/v1/admin/groups/:id/composite-routes/preview
|
||||
func (h *GroupHandler) PreviewCompositeRoute(c *gin.Context) {
|
||||
groupID, ok := parsePositiveIDParam(c, "id")
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
var req CompositeRoutePreviewRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "Invalid request body: "+err.Error())
|
||||
return
|
||||
}
|
||||
decision, err := h.adminService.PreviewCompositeRoute(c.Request.Context(), groupID, service.CompositeRoutePreviewRequest{
|
||||
Model: req.Model,
|
||||
Endpoint: req.Endpoint,
|
||||
})
|
||||
if err != nil {
|
||||
response.ErrorFrom(c, err)
|
||||
return
|
||||
}
|
||||
response.Success(c, decision)
|
||||
}
|
||||
|
||||
func compositeRouteRequestToInput(req CompositeRouteRequest, defaultEnabled bool) service.CompositeRouteInput {
|
||||
enabled := defaultEnabled
|
||||
if req.Enabled != nil {
|
||||
enabled = *req.Enabled
|
||||
}
|
||||
return service.CompositeRouteInput{
|
||||
PublicModel: req.PublicModel,
|
||||
MatchType: req.MatchType,
|
||||
TargetPlatform: req.TargetPlatform,
|
||||
UpstreamModel: req.UpstreamModel,
|
||||
Endpoint: req.Endpoint,
|
||||
Priority: req.Priority,
|
||||
Enabled: enabled,
|
||||
Notes: req.Notes,
|
||||
}
|
||||
}
|
||||
|
||||
func parsePositiveIDParam(c *gin.Context, name string) (int64, bool) {
|
||||
raw := c.Param(name)
|
||||
id, err := strconv.ParseInt(raw, 10, 64)
|
||||
if err != nil || id <= 0 {
|
||||
response.BadRequest(c, "Invalid "+name)
|
||||
return 0, false
|
||||
}
|
||||
return id, true
|
||||
}
|
||||
|
||||
// GetAll handles getting all active groups without pagination.
|
||||
// Pass ?include_inactive=true to also include disabled groups (used by the
|
||||
// API Key group filter, which needs to surface groups that still have API keys
|
||||
|
||||
@@ -187,6 +187,7 @@ func newTestGatewayHandler(t *testing.T, group *service.Group, accounts []*servi
|
||||
nil, // tlsFPProfileService
|
||||
nil, // channelService
|
||||
nil, // resolver
|
||||
nil, // compositeResolver
|
||||
nil, // balanceNotifyService
|
||||
nil, // userPlatformQuotaRepo
|
||||
)
|
||||
|
||||
@@ -56,7 +56,7 @@ func newGatewayModelsHandlerForTest(repo service.AccountRepository) *GatewayHand
|
||||
gatewayService: service.NewGatewayService(
|
||||
repo,
|
||||
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
|
||||
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
|
||||
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,6 +98,9 @@ func (h *GatewayHandler) GeminiV1BetaGetModel(c *gin.Context) {
|
||||
googleError(c, http.StatusBadRequest, "Missing model in URL")
|
||||
return
|
||||
}
|
||||
if resolvedModel, ok := service.ResolvedUpstreamModelFromContext(c.Request.Context()); ok && strings.TrimSpace(resolvedModel) != "" {
|
||||
modelName = strings.TrimSpace(resolvedModel)
|
||||
}
|
||||
|
||||
// 强制 antigravity 模式:返回 antigravity 模型信息
|
||||
if forcePlatform == service.PlatformAntigravity {
|
||||
@@ -166,6 +169,9 @@ func (h *GatewayHandler) GeminiV1BetaModels(c *gin.Context) {
|
||||
googleError(c, http.StatusNotFound, err.Error())
|
||||
return
|
||||
}
|
||||
if resolvedModel, ok := service.ResolvedUpstreamModelFromContext(c.Request.Context()); ok && strings.TrimSpace(resolvedModel) != "" {
|
||||
modelName = strings.TrimSpace(resolvedModel)
|
||||
}
|
||||
|
||||
stream := action == "streamGenerateContent"
|
||||
reqLog = reqLog.With(zap.String("model", modelName), zap.String("action", action), zap.Bool("stream", stream))
|
||||
|
||||
@@ -1160,7 +1160,7 @@ func logOpsStreamError(c *gin.Context, ops *service.OpsService, wireStatus int)
|
||||
}
|
||||
|
||||
fallbackPlatform := guessPlatformFromPath(c.Request.URL.Path)
|
||||
platform := resolveOpsPlatform(apiKey, fallbackPlatform)
|
||||
platform := resolveOpsPlatform(c.Request.Context(), apiKey, fallbackPlatform)
|
||||
|
||||
requestID := c.Writer.Header().Get("X-Request-Id")
|
||||
if requestID == "" {
|
||||
|
||||
@@ -11,6 +11,15 @@ const (
|
||||
// ResolvedTargetPlatform 是 composite 分组按请求模型解析出的真实目标平台。
|
||||
ResolvedTargetPlatform Key = "ctx_resolved_target_platform"
|
||||
|
||||
// ResolvedUpstreamModel 是 composite 分组将公开模型名解析到的上游模型名。
|
||||
ResolvedUpstreamModel Key = "ctx_resolved_upstream_model"
|
||||
|
||||
// RequestedPublicModel 是客户端原始请求中的公开模型名。
|
||||
RequestedPublicModel Key = "ctx_requested_public_model"
|
||||
|
||||
// CompositeRouteSource 标识 composite 解析结果来自显式路由还是内置模型探测。
|
||||
CompositeRouteSource Key = "ctx_composite_route_source"
|
||||
|
||||
// RequestID 为服务端生成/透传的请求 ID。
|
||||
RequestID Key = "ctx_request_id"
|
||||
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
dbent "github.com/Wei-Shaw/sub2api/ent"
|
||||
"github.com/Wei-Shaw/sub2api/ent/compositemodelroute"
|
||||
"github.com/Wei-Shaw/sub2api/internal/service"
|
||||
)
|
||||
|
||||
type compositeModelRouteRepository struct {
|
||||
client *dbent.Client
|
||||
}
|
||||
|
||||
func NewCompositeModelRouteRepository(client *dbent.Client) service.CompositeModelRouteRepository {
|
||||
return &compositeModelRouteRepository{client: client}
|
||||
}
|
||||
|
||||
func (r *compositeModelRouteRepository) ListByGroup(ctx context.Context, groupID int64, includeDisabled bool) ([]service.CompositeModelRoute, error) {
|
||||
q := clientFromContext(ctx, r.client).CompositeModelRoute.Query().
|
||||
Where(compositemodelroute.GroupIDEQ(groupID)).
|
||||
Order(
|
||||
dbent.Asc(compositemodelroute.FieldPriority),
|
||||
dbent.Asc(compositemodelroute.FieldID),
|
||||
)
|
||||
if !includeDisabled {
|
||||
q = q.Where(compositemodelroute.EnabledEQ(true))
|
||||
}
|
||||
rows, err := q.All(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make([]service.CompositeModelRoute, 0, len(rows))
|
||||
for _, row := range rows {
|
||||
out = append(out, *compositeModelRouteEntityToService(row))
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (r *compositeModelRouteRepository) Create(ctx context.Context, route *service.CompositeModelRoute) error {
|
||||
if route == nil {
|
||||
return service.ErrCompositeRouteNotFound
|
||||
}
|
||||
created, err := clientFromContext(ctx, r.client).CompositeModelRoute.Create().
|
||||
SetGroupID(route.GroupID).
|
||||
SetPublicModel(route.PublicModel).
|
||||
SetMatchType(route.MatchType).
|
||||
SetTargetPlatform(route.TargetPlatform).
|
||||
SetUpstreamModel(route.UpstreamModel).
|
||||
SetEndpoint(route.Endpoint).
|
||||
SetPriority(route.Priority).
|
||||
SetEnabled(route.Enabled).
|
||||
SetNotes(route.Notes).
|
||||
Save(ctx)
|
||||
if err != nil {
|
||||
return translatePersistenceError(err, nil, service.ErrCompositeRouteExists)
|
||||
}
|
||||
*route = *compositeModelRouteEntityToService(created)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *compositeModelRouteRepository) Update(ctx context.Context, route *service.CompositeModelRoute) error {
|
||||
if route == nil {
|
||||
return service.ErrCompositeRouteNotFound
|
||||
}
|
||||
updated, err := clientFromContext(ctx, r.client).CompositeModelRoute.UpdateOneID(route.ID).
|
||||
SetPublicModel(route.PublicModel).
|
||||
SetMatchType(route.MatchType).
|
||||
SetTargetPlatform(route.TargetPlatform).
|
||||
SetUpstreamModel(route.UpstreamModel).
|
||||
SetEndpoint(route.Endpoint).
|
||||
SetPriority(route.Priority).
|
||||
SetEnabled(route.Enabled).
|
||||
SetNotes(route.Notes).
|
||||
Save(ctx)
|
||||
if err != nil {
|
||||
return translatePersistenceError(err, service.ErrCompositeRouteNotFound, service.ErrCompositeRouteExists)
|
||||
}
|
||||
*route = *compositeModelRouteEntityToService(updated)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *compositeModelRouteRepository) Delete(ctx context.Context, id int64) error {
|
||||
err := clientFromContext(ctx, r.client).CompositeModelRoute.DeleteOneID(id).Exec(ctx)
|
||||
return translatePersistenceError(err, service.ErrCompositeRouteNotFound, nil)
|
||||
}
|
||||
|
||||
func (r *compositeModelRouteRepository) DeleteByGroup(ctx context.Context, groupID int64) error {
|
||||
_, err := clientFromContext(ctx, r.client).CompositeModelRoute.Delete().
|
||||
Where(compositemodelroute.GroupIDEQ(groupID)).
|
||||
Exec(ctx)
|
||||
return err
|
||||
}
|
||||
|
||||
func compositeModelRouteEntityToService(row *dbent.CompositeModelRoute) *service.CompositeModelRoute {
|
||||
if row == nil {
|
||||
return nil
|
||||
}
|
||||
return &service.CompositeModelRoute{
|
||||
ID: row.ID,
|
||||
GroupID: row.GroupID,
|
||||
PublicModel: row.PublicModel,
|
||||
MatchType: row.MatchType,
|
||||
TargetPlatform: row.TargetPlatform,
|
||||
UpstreamModel: row.UpstreamModel,
|
||||
Endpoint: row.Endpoint,
|
||||
Priority: row.Priority,
|
||||
Enabled: row.Enabled,
|
||||
Notes: derefString(row.Notes),
|
||||
CreatedAt: row.CreatedAt,
|
||||
UpdatedAt: row.UpdatedAt,
|
||||
}
|
||||
}
|
||||
@@ -835,7 +835,12 @@ func (r *groupRepository) DeleteCascade(ctx context.Context, id int64) ([]int64,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 4. Soft-delete group itself.
|
||||
// 4. Soft-delete composite model routes owned by this group.
|
||||
if _, err := exec.ExecContext(ctx, "UPDATE composite_model_routes SET deleted_at = NOW() WHERE group_id = $1 AND deleted_at IS NULL", id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 5. Soft-delete group itself.
|
||||
if _, err := txClient.Group.Delete().Where(group.IDEQ(id)).Exec(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -69,6 +69,7 @@ var ProviderSet = wire.NewSet(
|
||||
NewAPIKeyRepository,
|
||||
NewGroupRepository,
|
||||
NewAdminGroupRepository,
|
||||
NewCompositeModelRouteRepository,
|
||||
NewAccountRepository,
|
||||
NewAdminAccountRepository,
|
||||
NewScheduledTestPlanRepository, // 定时测试计划仓储
|
||||
|
||||
@@ -1396,7 +1396,7 @@ func newContractDeps(t *testing.T) *contractDeps {
|
||||
settingRepo := newStubSettingRepo()
|
||||
settingService := service.NewSettingService(settingRepo, cfg)
|
||||
|
||||
adminService := service.NewAdminService(userRepo, groupRepo, &accountRepo, proxyRepo, apiKeyRepo, redeemRepo, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil)
|
||||
adminService := service.NewAdminService(userRepo, groupRepo, &accountRepo, proxyRepo, apiKeyRepo, redeemRepo, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil)
|
||||
authHandler := handler.NewAuthHandler(cfg, nil, userService, settingService, nil, redeemService, nil, nil)
|
||||
apiKeyHandler := handler.NewAPIKeyHandler(apiKeyService)
|
||||
usageHandler := handler.NewUsageHandler(usageService, apiKeyService, nil, nil)
|
||||
|
||||
@@ -39,6 +39,7 @@ func ProvideRouter(
|
||||
subscriptionService *service.SubscriptionService,
|
||||
opsService *service.OpsService,
|
||||
settingService *service.SettingService,
|
||||
compositeResolver *service.CompositeRouteResolver,
|
||||
redisClient *redis.Client,
|
||||
) *gin.Engine {
|
||||
if cfg.Server.Mode == "release" {
|
||||
@@ -85,7 +86,7 @@ func ProvideRouter(
|
||||
service.SetWebSearchManager(websearch.NewManager(configs, redisClient))
|
||||
})
|
||||
|
||||
return SetupRouter(r, handlers, jwtAuth, adminAuth, apiKeyAuth, auditLog, stepUpAuth, apiKeyService, subscriptionService, opsService, settingService, cfg, redisClient)
|
||||
return SetupRouter(r, handlers, jwtAuth, adminAuth, apiKeyAuth, auditLog, stepUpAuth, apiKeyService, subscriptionService, opsService, settingService, compositeResolver, cfg, redisClient)
|
||||
}
|
||||
|
||||
func configureTrustedProxies(r *gin.Engine, cfg config.ServerConfig) {
|
||||
|
||||
@@ -32,6 +32,7 @@ func SetupRouter(
|
||||
subscriptionService *service.SubscriptionService,
|
||||
opsService *service.OpsService,
|
||||
settingService *service.SettingService,
|
||||
compositeResolver *service.CompositeRouteResolver,
|
||||
cfg *config.Config,
|
||||
redisClient *redis.Client,
|
||||
) *gin.Engine {
|
||||
@@ -88,7 +89,7 @@ func SetupRouter(
|
||||
}
|
||||
|
||||
// 注册路由
|
||||
registerRoutes(r, handlers, jwtAuth, adminAuth, apiKeyAuth, auditLog, stepUpAuth, apiKeyService, subscriptionService, opsService, settingService, cfg, redisClient)
|
||||
registerRoutes(r, handlers, jwtAuth, adminAuth, apiKeyAuth, auditLog, stepUpAuth, apiKeyService, subscriptionService, opsService, settingService, compositeResolver, cfg, redisClient)
|
||||
|
||||
return r
|
||||
}
|
||||
@@ -106,6 +107,7 @@ func registerRoutes(
|
||||
subscriptionService *service.SubscriptionService,
|
||||
opsService *service.OpsService,
|
||||
settingService *service.SettingService,
|
||||
compositeResolver *service.CompositeRouteResolver,
|
||||
cfg *config.Config,
|
||||
redisClient *redis.Client,
|
||||
) {
|
||||
@@ -119,7 +121,7 @@ func registerRoutes(
|
||||
routes.RegisterAuthRoutes(v1, h, jwtAuth, auditLog, redisClient, settingService)
|
||||
routes.RegisterUserRoutes(v1, h, jwtAuth, auditLog, settingService)
|
||||
routes.RegisterAdminRoutes(v1, h, adminAuth, auditLog, stepUpAuth, settingService)
|
||||
routes.RegisterGatewayRoutes(r, h, apiKeyAuth, apiKeyService, subscriptionService, opsService, settingService, cfg)
|
||||
routes.RegisterGatewayRoutes(r, h, apiKeyAuth, apiKeyService, subscriptionService, opsService, settingService, compositeResolver, cfg)
|
||||
routes.RegisterPaymentRoutes(v1, h.Payment, h.PaymentWebhook, h.Admin.Payment, jwtAuth, adminAuth, auditLog, settingService)
|
||||
|
||||
handler.RegisterPageRoutes(v1, cfg.Pricing.DataDir, gin.HandlerFunc(jwtAuth), gin.HandlerFunc(adminAuth), settingService)
|
||||
|
||||
@@ -319,6 +319,11 @@ func registerGroupRoutes(admin *gin.RouterGroup, h *handler.Handlers) {
|
||||
groups.GET("/capacity-summary", h.Admin.Group.GetCapacitySummary)
|
||||
groups.PUT("/sort-order", h.Admin.Group.UpdateSortOrder)
|
||||
groups.GET("/:id/models-list-candidates", h.Admin.Group.GetModelsListCandidates)
|
||||
groups.GET("/:id/composite-routes", h.Admin.Group.ListCompositeRoutes)
|
||||
groups.POST("/:id/composite-routes", h.Admin.Group.CreateCompositeRoute)
|
||||
groups.POST("/:id/composite-routes/preview", h.Admin.Group.PreviewCompositeRoute)
|
||||
groups.PUT("/:id/composite-routes/:route_id", h.Admin.Group.UpdateCompositeRoute)
|
||||
groups.DELETE("/:id/composite-routes/:route_id", h.Admin.Group.DeleteCompositeRoute)
|
||||
groups.GET("/:id", h.Admin.Group.GetByID)
|
||||
groups.POST("", h.Admin.Group.Create)
|
||||
groups.POST("/:id/duplicate", h.Admin.Group.Duplicate)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
@@ -13,6 +14,40 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type compositeRouteRepoStub struct {
|
||||
routes []service.CompositeModelRoute
|
||||
}
|
||||
|
||||
func (s compositeRouteRepoStub) ListByGroup(ctx context.Context, groupID int64, includeDisabled bool) ([]service.CompositeModelRoute, error) {
|
||||
routes := make([]service.CompositeModelRoute, 0, len(s.routes))
|
||||
for _, route := range s.routes {
|
||||
if route.GroupID != groupID {
|
||||
continue
|
||||
}
|
||||
if !includeDisabled && !route.Enabled {
|
||||
continue
|
||||
}
|
||||
routes = append(routes, route)
|
||||
}
|
||||
return routes, nil
|
||||
}
|
||||
|
||||
func (s compositeRouteRepoStub) Create(ctx context.Context, route *service.CompositeModelRoute) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s compositeRouteRepoStub) Update(ctx context.Context, route *service.CompositeModelRoute) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s compositeRouteRepoStub) Delete(ctx context.Context, id int64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s compositeRouteRepoStub) DeleteByGroup(ctx context.Context, groupID int64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestCompositeTargetPlatformMiddlewareResolvesModelAndRestoresBody(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
router := gin.New()
|
||||
@@ -24,7 +59,7 @@ func TestCompositeTargetPlatformMiddlewareResolvesModelAndRestoresBody(t *testin
|
||||
})
|
||||
c.Next()
|
||||
})))
|
||||
router.Use(compositeTargetPlatformMiddleware())
|
||||
router.Use(compositeTargetPlatformMiddleware(nil))
|
||||
router.POST("/", func(c *gin.Context) {
|
||||
platform, ok := service.ResolvedTargetPlatformFromContext(c.Request.Context())
|
||||
require.True(t, ok)
|
||||
@@ -44,3 +79,101 @@ func TestCompositeTargetPlatformMiddlewareResolvesModelAndRestoresBody(t *testin
|
||||
|
||||
require.Equal(t, http.StatusNoContent, w.Code)
|
||||
}
|
||||
|
||||
func TestCompositeTargetPlatformMiddlewareUsesExplicitRouteAndRewritesBody(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
router := gin.New()
|
||||
resolver := service.NewCompositeRouteResolver(compositeRouteRepoStub{
|
||||
routes: []service.CompositeModelRoute{
|
||||
{
|
||||
ID: 1,
|
||||
GroupID: 1,
|
||||
PublicModel: "openrouter/gpt-5",
|
||||
MatchType: service.CompositeRouteMatchExact,
|
||||
TargetPlatform: service.PlatformOpenAI,
|
||||
UpstreamModel: "gpt-5",
|
||||
Endpoint: service.CompositeRouteEndpointAny,
|
||||
Priority: 100,
|
||||
Enabled: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
router.Use(gin.HandlerFunc(servermiddleware.APIKeyAuthMiddleware(func(c *gin.Context) {
|
||||
groupID := int64(1)
|
||||
c.Set(string(servermiddleware.ContextKeyAPIKey), &service.APIKey{
|
||||
GroupID: &groupID,
|
||||
Group: &service.Group{ID: groupID, Platform: service.PlatformComposite},
|
||||
})
|
||||
c.Next()
|
||||
})))
|
||||
router.Use(compositeTargetPlatformMiddleware(resolver))
|
||||
router.POST("/v1/chat/completions", func(c *gin.Context) {
|
||||
platform, ok := service.ResolvedTargetPlatformFromContext(c.Request.Context())
|
||||
require.True(t, ok)
|
||||
require.Equal(t, service.PlatformOpenAI, platform)
|
||||
|
||||
upstreamModel, ok := service.ResolvedUpstreamModelFromContext(c.Request.Context())
|
||||
require.True(t, ok)
|
||||
require.Equal(t, "gpt-5", upstreamModel)
|
||||
|
||||
body, err := io.ReadAll(c.Request.Body)
|
||||
require.NoError(t, err)
|
||||
require.JSONEq(t, `{"model":"gpt-5","messages":[]}`, string(body))
|
||||
c.Status(http.StatusNoContent)
|
||||
})
|
||||
|
||||
req := httptest.NewRequest(http.MethodPost, "/v1/chat/completions", strings.NewReader(`{"model":"openrouter/gpt-5","messages":[]}`))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
require.Equal(t, http.StatusNoContent, w.Code)
|
||||
}
|
||||
|
||||
func TestCompositeGeminiTargetPlatformMiddlewareUsesPathRoute(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
router := gin.New()
|
||||
resolver := service.NewCompositeRouteResolver(compositeRouteRepoStub{
|
||||
routes: []service.CompositeModelRoute{
|
||||
{
|
||||
ID: 1,
|
||||
GroupID: 1,
|
||||
PublicModel: "openrouter/gemini-pro",
|
||||
MatchType: service.CompositeRouteMatchExact,
|
||||
TargetPlatform: service.PlatformGemini,
|
||||
UpstreamModel: "gemini-2.5-pro",
|
||||
Endpoint: service.CompositeRouteEndpointGemini,
|
||||
Priority: 100,
|
||||
Enabled: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
router.Use(gin.HandlerFunc(servermiddleware.APIKeyAuthMiddleware(func(c *gin.Context) {
|
||||
groupID := int64(1)
|
||||
c.Set(string(servermiddleware.ContextKeyAPIKey), &service.APIKey{
|
||||
GroupID: &groupID,
|
||||
Group: &service.Group{ID: groupID, Platform: service.PlatformComposite},
|
||||
})
|
||||
c.Next()
|
||||
})))
|
||||
router.Use(compositeGeminiTargetPlatformMiddleware(resolver))
|
||||
router.POST("/v1beta/models/*modelAction", func(c *gin.Context) {
|
||||
platform, ok := service.ResolvedTargetPlatformFromContext(c.Request.Context())
|
||||
require.True(t, ok)
|
||||
require.Equal(t, service.PlatformGemini, platform)
|
||||
|
||||
upstreamModel, ok := service.ResolvedUpstreamModelFromContext(c.Request.Context())
|
||||
require.True(t, ok)
|
||||
require.Equal(t, "gemini-2.5-pro", upstreamModel)
|
||||
c.Status(http.StatusNoContent)
|
||||
})
|
||||
|
||||
req := httptest.NewRequest(http.MethodPost, "/v1beta/models/openrouter/gemini-pro:generateContent", strings.NewReader(`{"contents":[]}`))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
require.Equal(t, http.StatusNoContent, w.Code)
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/tidwall/gjson"
|
||||
"github.com/tidwall/sjson"
|
||||
)
|
||||
|
||||
// RegisterGatewayRoutes 注册 API 网关路由(Claude/OpenAI/Gemini 兼容)
|
||||
@@ -27,6 +28,7 @@ func RegisterGatewayRoutes(
|
||||
subscriptionService *service.SubscriptionService,
|
||||
opsService *service.OpsService,
|
||||
settingService *service.SettingService,
|
||||
compositeResolver *service.CompositeRouteResolver,
|
||||
cfg *config.Config,
|
||||
) {
|
||||
bodyLimit := middleware.RequestBodyLimit(cfg.Gateway.MaxBodySize)
|
||||
@@ -34,8 +36,8 @@ func RegisterGatewayRoutes(
|
||||
clientRequestID := middleware.ClientRequestID()
|
||||
opsErrorLogger := handler.OpsErrorLoggerMiddleware(opsService)
|
||||
endpointNorm := handler.InboundEndpointMiddleware()
|
||||
compositeTarget := compositeTargetPlatformMiddleware()
|
||||
compositeGeminiTarget := compositeImplicitTargetPlatformMiddleware(service.PlatformGemini)
|
||||
compositeTarget := compositeTargetPlatformMiddleware(compositeResolver)
|
||||
compositeGeminiTarget := compositeGeminiTargetPlatformMiddleware(compositeResolver)
|
||||
|
||||
// 未分组 Key 拦截中间件(按协议格式区分错误响应)
|
||||
requireGroupAnthropic := middleware.RequireGroupAssignment(settingService, middleware.AnthropicErrorWriter)
|
||||
@@ -356,7 +358,10 @@ func getGroupPlatform(c *gin.Context) string {
|
||||
return apiKey.Group.Platform
|
||||
}
|
||||
|
||||
func compositeTargetPlatformMiddleware() gin.HandlerFunc {
|
||||
func compositeTargetPlatformMiddleware(resolver *service.CompositeRouteResolver) gin.HandlerFunc {
|
||||
if resolver == nil {
|
||||
resolver = service.NewCompositeRouteResolver(nil)
|
||||
}
|
||||
return func(c *gin.Context) {
|
||||
apiKey, ok := middleware.GetAPIKeyFromContext(c)
|
||||
if !ok || apiKey == nil || apiKey.Group == nil || apiKey.Group.Platform != service.PlatformComposite {
|
||||
@@ -381,26 +386,71 @@ func compositeTargetPlatformMiddleware() gin.HandlerFunc {
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
resetRequestBody(c, body)
|
||||
|
||||
model := strings.TrimSpace(gjson.GetBytes(body, "model").String())
|
||||
if model != "" {
|
||||
if platform, ok := service.DetectModelPlatform(model); ok {
|
||||
c.Request = c.Request.WithContext(service.WithResolvedTargetPlatform(c.Request.Context(), platform))
|
||||
decision, err := resolver.Resolve(c.Request.Context(), apiKey.Group.ID, model, compositeRouteEndpointForPath(c.Request.URL.Path))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": gin.H{"type": "server_error", "message": "Failed to resolve composite model route"}})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
if decision.Matched {
|
||||
c.Request = c.Request.WithContext(service.WithCompositeRouteDecision(c.Request.Context(), decision))
|
||||
if upstreamModel := strings.TrimSpace(decision.UpstreamModel); upstreamModel != "" && upstreamModel != model && gjson.ValidBytes(body) {
|
||||
if rewritten, rewriteErr := sjson.SetBytes(body, "model", upstreamModel); rewriteErr == nil {
|
||||
body = rewritten
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
resetRequestBody(c, body)
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func compositeGeminiTargetPlatformMiddleware(resolver *service.CompositeRouteResolver) gin.HandlerFunc {
|
||||
if resolver == nil {
|
||||
resolver = service.NewCompositeRouteResolver(nil)
|
||||
}
|
||||
return func(c *gin.Context) {
|
||||
apiKey, ok := middleware.GetAPIKeyFromContext(c)
|
||||
if ok && apiKey != nil && apiKey.Group != nil && apiKey.Group.Platform == service.PlatformComposite {
|
||||
model := compositeGeminiModelFromParams(c)
|
||||
if model != "" {
|
||||
decision, err := resolver.Resolve(c.Request.Context(), apiKey.Group.ID, model, service.CompositeRouteEndpointGemini)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": gin.H{"type": "server_error", "message": "Failed to resolve composite model route"}})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
if decision.Matched {
|
||||
c.Request = c.Request.WithContext(service.WithCompositeRouteDecision(c.Request.Context(), decision))
|
||||
}
|
||||
}
|
||||
if _, resolved := service.ResolvedTargetPlatformFromContext(c.Request.Context()); !resolved {
|
||||
c.Request = c.Request.WithContext(service.WithResolvedTargetPlatform(c.Request.Context(), service.PlatformGemini))
|
||||
}
|
||||
}
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func compositeImplicitTargetPlatformMiddleware(platform string) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
apiKey, ok := middleware.GetAPIKeyFromContext(c)
|
||||
if ok && apiKey != nil && apiKey.Group != nil && apiKey.Group.Platform == service.PlatformComposite {
|
||||
c.Request = c.Request.WithContext(service.WithResolvedTargetPlatform(c.Request.Context(), platform))
|
||||
}
|
||||
c.Next()
|
||||
func compositeGeminiModelFromParams(c *gin.Context) string {
|
||||
if c == nil {
|
||||
return ""
|
||||
}
|
||||
if model := strings.TrimSpace(c.Param("model")); model != "" {
|
||||
return model
|
||||
}
|
||||
modelAction := strings.TrimPrefix(strings.TrimSpace(c.Param("modelAction")), "/")
|
||||
if modelAction == "" {
|
||||
return ""
|
||||
}
|
||||
if idx := strings.LastIndex(modelAction, ":"); idx >= 0 {
|
||||
return strings.TrimSpace(modelAction[:idx])
|
||||
}
|
||||
return modelAction
|
||||
}
|
||||
|
||||
func resetRequestBody(c *gin.Context, body []byte) {
|
||||
@@ -408,3 +458,24 @@ func resetRequestBody(c *gin.Context, body []byte) {
|
||||
c.Request.ContentLength = int64(len(body))
|
||||
c.Request.Header.Set("Content-Length", strconv.Itoa(len(body)))
|
||||
}
|
||||
|
||||
func compositeRouteEndpointForPath(path string) string {
|
||||
switch {
|
||||
case strings.Contains(path, "/messages/count_tokens"):
|
||||
return service.CompositeRouteEndpointCountTokens
|
||||
case strings.Contains(path, "/messages"):
|
||||
return service.CompositeRouteEndpointMessages
|
||||
case strings.Contains(path, "/responses"):
|
||||
return service.CompositeRouteEndpointResponses
|
||||
case strings.Contains(path, "/chat/completions"):
|
||||
return service.CompositeRouteEndpointChatCompletions
|
||||
case strings.Contains(path, "/embeddings"):
|
||||
return service.CompositeRouteEndpointEmbeddings
|
||||
case strings.Contains(path, "/images/"):
|
||||
return service.CompositeRouteEndpointImages
|
||||
case strings.Contains(path, "/v1beta/"):
|
||||
return service.CompositeRouteEndpointGemini
|
||||
default:
|
||||
return service.CompositeRouteEndpointAny
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ func newGatewayRoutesTestRouterWithConfig(cfg *config.Config, platform ...string
|
||||
if len(platform) > 0 && platform[0] != "" {
|
||||
groupPlatform = platform[0]
|
||||
}
|
||||
|
||||
RegisterGatewayRoutes(
|
||||
router,
|
||||
&handler.Handlers{
|
||||
@@ -47,6 +46,7 @@ func newGatewayRoutesTestRouterWithConfig(cfg *config.Config, platform ...string
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
cfg,
|
||||
)
|
||||
|
||||
@@ -206,6 +206,24 @@ func TestGatewayRoutesNonGrokVideosAreRejectedAtPlatformGate(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGatewayRoutesCompositeOpenAIOnlyEndpointsRequireOpenAITarget(t *testing.T) {
|
||||
router := newGatewayRoutesTestRouter(service.PlatformComposite)
|
||||
|
||||
req := httptest.NewRequest(http.MethodPost, "/v1/embeddings", strings.NewReader(`{"model":"gemini-2.5-pro","input":"hello"}`))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
router.ServeHTTP(w, req)
|
||||
require.Equal(t, http.StatusNotFound, w.Code)
|
||||
|
||||
req = httptest.NewRequest(http.MethodPost, "/v1/embeddings", strings.NewReader(`{"model":"text-embedding-3-small","input":"hello"}`))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
w = httptest.NewRecorder()
|
||||
|
||||
router.ServeHTTP(w, req)
|
||||
require.NotEqual(t, http.StatusNotFound, w.Code)
|
||||
}
|
||||
|
||||
func TestGatewayRoutesGrokAllowsCLICompatibilityEntrypoints(t *testing.T) {
|
||||
router := newGatewayRoutesTestRouter(service.PlatformGrok)
|
||||
|
||||
|
||||
@@ -100,6 +100,134 @@ func (s *adminServiceImpl) GetGroupModelsListCandidates(ctx context.Context, id
|
||||
return candidates, nil
|
||||
}
|
||||
|
||||
func (s *adminServiceImpl) ListCompositeRoutes(ctx context.Context, groupID int64) ([]CompositeModelRoute, error) {
|
||||
if err := s.requireCompositeGroup(ctx, groupID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if s.compositeRouteRepo == nil {
|
||||
return nil, fmt.Errorf("composite route repository is not configured")
|
||||
}
|
||||
return s.compositeRouteRepo.ListByGroup(ctx, groupID, true)
|
||||
}
|
||||
|
||||
func (s *adminServiceImpl) CreateCompositeRoute(ctx context.Context, groupID int64, input CompositeRouteInput) (*CompositeModelRoute, error) {
|
||||
if err := s.requireCompositeGroup(ctx, groupID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if s.compositeRouteRepo == nil {
|
||||
return nil, fmt.Errorf("composite route repository is not configured")
|
||||
}
|
||||
route, err := compositeRouteFromInput(groupID, input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := s.compositeRouteRepo.Create(ctx, route); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return route, nil
|
||||
}
|
||||
|
||||
func (s *adminServiceImpl) UpdateCompositeRoute(ctx context.Context, groupID, routeID int64, input CompositeRouteInput) (*CompositeModelRoute, error) {
|
||||
if err := s.requireCompositeGroup(ctx, groupID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if s.compositeRouteRepo == nil {
|
||||
return nil, fmt.Errorf("composite route repository is not configured")
|
||||
}
|
||||
if ok, err := s.compositeRouteBelongsToGroup(ctx, groupID, routeID); err != nil {
|
||||
return nil, err
|
||||
} else if !ok {
|
||||
return nil, ErrCompositeRouteNotFound
|
||||
}
|
||||
route, err := compositeRouteFromInput(groupID, input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
route.ID = routeID
|
||||
if err := s.compositeRouteRepo.Update(ctx, route); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return route, nil
|
||||
}
|
||||
|
||||
func (s *adminServiceImpl) DeleteCompositeRoute(ctx context.Context, groupID, routeID int64) error {
|
||||
if err := s.requireCompositeGroup(ctx, groupID); err != nil {
|
||||
return err
|
||||
}
|
||||
if s.compositeRouteRepo == nil {
|
||||
return fmt.Errorf("composite route repository is not configured")
|
||||
}
|
||||
if ok, err := s.compositeRouteBelongsToGroup(ctx, groupID, routeID); err != nil {
|
||||
return err
|
||||
} else if !ok {
|
||||
return ErrCompositeRouteNotFound
|
||||
}
|
||||
return s.compositeRouteRepo.Delete(ctx, routeID)
|
||||
}
|
||||
|
||||
func (s *adminServiceImpl) PreviewCompositeRoute(ctx context.Context, groupID int64, input CompositeRoutePreviewRequest) (*CompositeRouteDecision, error) {
|
||||
if err := s.requireCompositeGroup(ctx, groupID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resolver := s.compositeResolver
|
||||
if resolver == nil {
|
||||
resolver = NewCompositeRouteResolver(s.compositeRouteRepo)
|
||||
}
|
||||
decision, err := resolver.Resolve(ctx, groupID, input.Model, input.Endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &decision, nil
|
||||
}
|
||||
|
||||
func (s *adminServiceImpl) requireCompositeGroup(ctx context.Context, groupID int64) error {
|
||||
group, err := s.groupRepo.GetByIDLite(ctx, groupID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if group.Platform != PlatformComposite {
|
||||
return fmt.Errorf("group %d is not a composite group", groupID)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *adminServiceImpl) compositeRouteBelongsToGroup(ctx context.Context, groupID, routeID int64) (bool, error) {
|
||||
routes, err := s.compositeRouteRepo.ListByGroup(ctx, groupID, true)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
for i := range routes {
|
||||
if routes[i].ID == routeID {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func compositeRouteFromInput(groupID int64, input CompositeRouteInput) (*CompositeModelRoute, error) {
|
||||
input = normalizeCompositeRouteInput(input)
|
||||
if input.PublicModel == "" {
|
||||
return nil, fmt.Errorf("public_model is required")
|
||||
}
|
||||
if !isConcreteRequestPlatform(input.TargetPlatform) {
|
||||
return nil, fmt.Errorf("target_platform must be a concrete provider")
|
||||
}
|
||||
if input.Priority == 0 {
|
||||
input.Priority = 100
|
||||
}
|
||||
return &CompositeModelRoute{
|
||||
GroupID: groupID,
|
||||
PublicModel: input.PublicModel,
|
||||
MatchType: input.MatchType,
|
||||
TargetPlatform: input.TargetPlatform,
|
||||
UpstreamModel: input.UpstreamModel,
|
||||
Endpoint: input.Endpoint,
|
||||
Priority: input.Priority,
|
||||
Enabled: input.Enabled,
|
||||
Notes: input.Notes,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func defaultModelsListCandidateIDs(platform string) []string {
|
||||
switch platform {
|
||||
case PlatformOpenAI:
|
||||
|
||||
@@ -48,6 +48,11 @@ type AdminService interface {
|
||||
RecoverDuplicateGroup(ctx context.Context, id int64, actorScope, operationKey string) (*Group, error)
|
||||
UpdateGroup(ctx context.Context, id int64, input *UpdateGroupInput) (*Group, error)
|
||||
DeleteGroup(ctx context.Context, id int64) error
|
||||
ListCompositeRoutes(ctx context.Context, groupID int64) ([]CompositeModelRoute, error)
|
||||
CreateCompositeRoute(ctx context.Context, groupID int64, input CompositeRouteInput) (*CompositeModelRoute, error)
|
||||
UpdateCompositeRoute(ctx context.Context, groupID, routeID int64, input CompositeRouteInput) (*CompositeModelRoute, error)
|
||||
DeleteCompositeRoute(ctx context.Context, groupID, routeID int64) error
|
||||
PreviewCompositeRoute(ctx context.Context, groupID int64, input CompositeRoutePreviewRequest) (*CompositeRouteDecision, error)
|
||||
GetGroupAPIKeys(ctx context.Context, groupID int64, page, pageSize int) ([]APIKey, int64, error)
|
||||
GetGroupRateMultipliers(ctx context.Context, groupID int64) ([]UserGroupRateEntry, error)
|
||||
ClearGroupRateMultipliers(ctx context.Context, groupID int64) error
|
||||
@@ -626,6 +631,8 @@ type adminServiceImpl struct {
|
||||
privacyClientFactory PrivacyClientFactory
|
||||
runtimeBlocker AccountRuntimeBlocker
|
||||
affiliateService adminRechargeAffiliateAccruer
|
||||
compositeRouteRepo CompositeModelRouteRepository
|
||||
compositeResolver *CompositeRouteResolver
|
||||
}
|
||||
|
||||
type adminRechargeAffiliateAccruer interface {
|
||||
@@ -657,6 +664,8 @@ func NewAdminService(
|
||||
privacyClientFactory PrivacyClientFactory,
|
||||
runtimeBlocker AccountRuntimeBlocker,
|
||||
affiliateService *AffiliateService,
|
||||
compositeRouteRepo CompositeModelRouteRepository,
|
||||
compositeResolver *CompositeRouteResolver,
|
||||
) AdminService {
|
||||
return &adminServiceImpl{
|
||||
userRepo: userRepo,
|
||||
@@ -680,5 +689,7 @@ func NewAdminService(
|
||||
privacyClientFactory: privacyClientFactory,
|
||||
runtimeBlocker: runtimeBlocker,
|
||||
affiliateService: affiliateService,
|
||||
compositeRouteRepo: compositeRouteRepo,
|
||||
compositeResolver: compositeResolver,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,6 +156,83 @@ func (s *groupRepoStubForAdmin) UpdateSortOrders(_ context.Context, _ []GroupSor
|
||||
return nil
|
||||
}
|
||||
|
||||
type compositeRouteRepoStubForAdmin struct {
|
||||
routes []CompositeModelRoute
|
||||
created *CompositeModelRoute
|
||||
updated *CompositeModelRoute
|
||||
deleted []int64
|
||||
nextID int64
|
||||
listErr error
|
||||
createErr error
|
||||
updateErr error
|
||||
deleteErr error
|
||||
}
|
||||
|
||||
func (s *compositeRouteRepoStubForAdmin) ListByGroup(_ context.Context, groupID int64, includeDisabled bool) ([]CompositeModelRoute, error) {
|
||||
if s.listErr != nil {
|
||||
return nil, s.listErr
|
||||
}
|
||||
routes := make([]CompositeModelRoute, 0, len(s.routes))
|
||||
for _, route := range s.routes {
|
||||
if route.GroupID != groupID {
|
||||
continue
|
||||
}
|
||||
if !includeDisabled && !route.Enabled {
|
||||
continue
|
||||
}
|
||||
routes = append(routes, route)
|
||||
}
|
||||
return routes, nil
|
||||
}
|
||||
|
||||
func (s *compositeRouteRepoStubForAdmin) Create(_ context.Context, route *CompositeModelRoute) error {
|
||||
if s.createErr != nil {
|
||||
return s.createErr
|
||||
}
|
||||
if s.nextID > 0 {
|
||||
route.ID = s.nextID
|
||||
}
|
||||
cloned := *route
|
||||
s.created = &cloned
|
||||
s.routes = append(s.routes, cloned)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *compositeRouteRepoStubForAdmin) Update(_ context.Context, route *CompositeModelRoute) error {
|
||||
if s.updateErr != nil {
|
||||
return s.updateErr
|
||||
}
|
||||
cloned := *route
|
||||
s.updated = &cloned
|
||||
for i := range s.routes {
|
||||
if s.routes[i].ID == route.ID {
|
||||
s.routes[i] = cloned
|
||||
return nil
|
||||
}
|
||||
}
|
||||
s.routes = append(s.routes, cloned)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *compositeRouteRepoStubForAdmin) Delete(_ context.Context, id int64) error {
|
||||
if s.deleteErr != nil {
|
||||
return s.deleteErr
|
||||
}
|
||||
s.deleted = append(s.deleted, id)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *compositeRouteRepoStubForAdmin) DeleteByGroup(_ context.Context, groupID int64) error {
|
||||
next := s.routes[:0]
|
||||
for _, route := range s.routes {
|
||||
if route.GroupID != groupID {
|
||||
next = append(next, route)
|
||||
}
|
||||
}
|
||||
s.routes = next
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestAdminService_ListGroups_PassesSortParams(t *testing.T) {
|
||||
repo := &groupRepoStubForAdmin{
|
||||
listWithFiltersGroups: []Group{{ID: 1, Name: "g1"}},
|
||||
@@ -1466,3 +1543,122 @@ func TestAdminService_UpdateGroup_InvalidRequestFallbackAllowsAntigravity(t *tes
|
||||
require.NotNil(t, repo.updated)
|
||||
require.Equal(t, fallbackID, *repo.updated.FallbackGroupIDOnInvalidRequest)
|
||||
}
|
||||
|
||||
func TestAdminService_CreateCompositeRoute_RejectsNonCompositeGroup(t *testing.T) {
|
||||
groupRepo := &groupRepoStubForAdmin{
|
||||
getByID: &Group{ID: 7, Platform: PlatformOpenAI},
|
||||
}
|
||||
routeRepo := &compositeRouteRepoStubForAdmin{}
|
||||
svc := &adminServiceImpl{groupRepo: groupRepo, compositeRouteRepo: routeRepo}
|
||||
|
||||
_, err := svc.CreateCompositeRoute(context.Background(), 7, CompositeRouteInput{
|
||||
PublicModel: "router/gpt-5",
|
||||
TargetPlatform: PlatformOpenAI,
|
||||
Enabled: true,
|
||||
})
|
||||
|
||||
require.Error(t, err)
|
||||
require.ErrorContains(t, err, "not a composite group")
|
||||
require.Nil(t, routeRepo.created)
|
||||
}
|
||||
|
||||
func TestAdminService_CreateCompositeRoute_NormalizesAndPersists(t *testing.T) {
|
||||
groupRepo := &groupRepoStubForAdmin{
|
||||
getByID: &Group{ID: 7, Platform: PlatformComposite},
|
||||
}
|
||||
routeRepo := &compositeRouteRepoStubForAdmin{nextID: 99}
|
||||
svc := &adminServiceImpl{groupRepo: groupRepo, compositeRouteRepo: routeRepo}
|
||||
|
||||
route, err := svc.CreateCompositeRoute(context.Background(), 7, CompositeRouteInput{
|
||||
PublicModel: " router/gpt- ",
|
||||
MatchType: CompositeRouteMatchPrefix,
|
||||
TargetPlatform: PlatformOpenAI,
|
||||
Endpoint: CompositeRouteEndpointResponses,
|
||||
Enabled: true,
|
||||
Notes: " route note ",
|
||||
})
|
||||
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, route)
|
||||
require.Equal(t, int64(99), route.ID)
|
||||
require.Equal(t, "router/gpt-", route.PublicModel)
|
||||
require.Equal(t, CompositeRouteMatchPrefix, route.MatchType)
|
||||
require.Equal(t, PlatformOpenAI, route.TargetPlatform)
|
||||
require.Equal(t, "router/gpt-", route.UpstreamModel)
|
||||
require.Equal(t, CompositeRouteEndpointResponses, route.Endpoint)
|
||||
require.Equal(t, 100, route.Priority)
|
||||
require.True(t, route.Enabled)
|
||||
require.Equal(t, "route note", route.Notes)
|
||||
require.Equal(t, route, routeRepo.created)
|
||||
}
|
||||
|
||||
func TestAdminService_UpdateAndDeleteCompositeRouteRequireRouteOwnership(t *testing.T) {
|
||||
groupRepo := &groupRepoStubForAdmin{
|
||||
getByID: &Group{ID: 7, Platform: PlatformComposite},
|
||||
}
|
||||
routeRepo := &compositeRouteRepoStubForAdmin{
|
||||
routes: []CompositeModelRoute{
|
||||
{ID: 11, GroupID: 7, PublicModel: "router/gpt-5", TargetPlatform: PlatformOpenAI, Enabled: true},
|
||||
{ID: 12, GroupID: 8, PublicModel: "router/other", TargetPlatform: PlatformGemini, Enabled: true},
|
||||
},
|
||||
}
|
||||
svc := &adminServiceImpl{groupRepo: groupRepo, compositeRouteRepo: routeRepo}
|
||||
|
||||
updated, err := svc.UpdateCompositeRoute(context.Background(), 7, 11, CompositeRouteInput{
|
||||
PublicModel: "router/gpt-5",
|
||||
TargetPlatform: PlatformGemini,
|
||||
UpstreamModel: "gemini-2.5-pro",
|
||||
Endpoint: CompositeRouteEndpointChatCompletions,
|
||||
Priority: 3,
|
||||
Enabled: true,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, int64(11), updated.ID)
|
||||
require.Equal(t, PlatformGemini, updated.TargetPlatform)
|
||||
require.Equal(t, "gemini-2.5-pro", updated.UpstreamModel)
|
||||
require.Equal(t, updated, routeRepo.updated)
|
||||
|
||||
err = svc.DeleteCompositeRoute(context.Background(), 7, 12)
|
||||
require.ErrorIs(t, err, ErrCompositeRouteNotFound)
|
||||
require.Empty(t, routeRepo.deleted)
|
||||
|
||||
err = svc.DeleteCompositeRoute(context.Background(), 7, 11)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, []int64{11}, routeRepo.deleted)
|
||||
}
|
||||
|
||||
func TestAdminService_PreviewCompositeRouteUsesExplicitRoutes(t *testing.T) {
|
||||
groupRepo := &groupRepoStubForAdmin{
|
||||
getByID: &Group{ID: 7, Platform: PlatformComposite},
|
||||
}
|
||||
routeRepo := &compositeRouteRepoStubForAdmin{
|
||||
routes: []CompositeModelRoute{
|
||||
{
|
||||
ID: 11,
|
||||
GroupID: 7,
|
||||
PublicModel: "openrouter/claude",
|
||||
MatchType: CompositeRouteMatchExact,
|
||||
TargetPlatform: PlatformAnthropic,
|
||||
UpstreamModel: "claude-sonnet-4-6",
|
||||
Endpoint: CompositeRouteEndpointMessages,
|
||||
Priority: 100,
|
||||
Enabled: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
svc := &adminServiceImpl{groupRepo: groupRepo, compositeRouteRepo: routeRepo}
|
||||
|
||||
decision, err := svc.PreviewCompositeRoute(context.Background(), 7, CompositeRoutePreviewRequest{
|
||||
Model: "openrouter/claude",
|
||||
Endpoint: CompositeRouteEndpointMessages,
|
||||
})
|
||||
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, decision)
|
||||
require.True(t, decision.Matched)
|
||||
require.Equal(t, CompositeRouteSourceExplicit, decision.Source)
|
||||
require.Equal(t, PlatformAnthropic, decision.TargetPlatform)
|
||||
require.Equal(t, "claude-sonnet-4-6", decision.UpstreamModel)
|
||||
require.NotNil(t, decision.Route)
|
||||
require.Equal(t, int64(11), decision.Route.ID)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
infraerrors "github.com/Wei-Shaw/sub2api/internal/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
CompositeRouteMatchExact = "exact"
|
||||
CompositeRouteMatchPrefix = "prefix"
|
||||
|
||||
CompositeRouteEndpointAny = "any"
|
||||
CompositeRouteEndpointMessages = "messages"
|
||||
CompositeRouteEndpointCountTokens = "count_tokens"
|
||||
CompositeRouteEndpointResponses = "responses"
|
||||
CompositeRouteEndpointChatCompletions = "chat_completions"
|
||||
CompositeRouteEndpointEmbeddings = "embeddings"
|
||||
CompositeRouteEndpointImages = "images"
|
||||
CompositeRouteEndpointGemini = "gemini"
|
||||
|
||||
CompositeRouteSourceExplicit = "route"
|
||||
CompositeRouteSourceDetector = "detector"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrCompositeRouteNotFound = infraerrors.NotFound("COMPOSITE_ROUTE_NOT_FOUND", "composite route not found")
|
||||
ErrCompositeRouteExists = infraerrors.Conflict("COMPOSITE_ROUTE_EXISTS", "composite route already exists")
|
||||
)
|
||||
|
||||
// CompositeModelRoute maps one public model identifier in a composite group to
|
||||
// the concrete provider/model that should handle the request.
|
||||
type CompositeModelRoute struct {
|
||||
ID int64 `json:"id"`
|
||||
GroupID int64 `json:"group_id"`
|
||||
PublicModel string `json:"public_model"`
|
||||
MatchType string `json:"match_type"`
|
||||
TargetPlatform string `json:"target_platform"`
|
||||
UpstreamModel string `json:"upstream_model"`
|
||||
Endpoint string `json:"endpoint"`
|
||||
Priority int `json:"priority"`
|
||||
Enabled bool `json:"enabled"`
|
||||
Notes string `json:"notes"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
type CompositeRoutePreviewRequest struct {
|
||||
Model string `json:"model"`
|
||||
Endpoint string `json:"endpoint"`
|
||||
}
|
||||
|
||||
type CompositeRouteDecision struct {
|
||||
Matched bool `json:"matched"`
|
||||
Source string `json:"source"`
|
||||
GroupID int64 `json:"group_id"`
|
||||
PublicModel string `json:"public_model"`
|
||||
TargetPlatform string `json:"target_platform"`
|
||||
UpstreamModel string `json:"upstream_model"`
|
||||
Endpoint string `json:"endpoint"`
|
||||
Route *CompositeModelRoute `json:"route,omitempty"`
|
||||
Reason string `json:"reason,omitempty"`
|
||||
}
|
||||
|
||||
type CompositeRouteInput struct {
|
||||
PublicModel string
|
||||
MatchType string
|
||||
TargetPlatform string
|
||||
UpstreamModel string
|
||||
Endpoint string
|
||||
Priority int
|
||||
Enabled bool
|
||||
Notes string
|
||||
}
|
||||
|
||||
type CompositeModelRouteRepository interface {
|
||||
ListByGroup(ctx context.Context, groupID int64, includeDisabled bool) ([]CompositeModelRoute, error)
|
||||
Create(ctx context.Context, route *CompositeModelRoute) error
|
||||
Update(ctx context.Context, route *CompositeModelRoute) error
|
||||
Delete(ctx context.Context, id int64) error
|
||||
DeleteByGroup(ctx context.Context, groupID int64) error
|
||||
}
|
||||
|
||||
func normalizeCompositeRouteEndpoint(endpoint string) string {
|
||||
endpoint = strings.ToLower(strings.TrimSpace(endpoint))
|
||||
if endpoint == "" {
|
||||
return CompositeRouteEndpointAny
|
||||
}
|
||||
switch endpoint {
|
||||
case CompositeRouteEndpointMessages,
|
||||
CompositeRouteEndpointCountTokens,
|
||||
CompositeRouteEndpointResponses,
|
||||
CompositeRouteEndpointChatCompletions,
|
||||
CompositeRouteEndpointEmbeddings,
|
||||
CompositeRouteEndpointImages,
|
||||
CompositeRouteEndpointGemini:
|
||||
return endpoint
|
||||
default:
|
||||
return CompositeRouteEndpointAny
|
||||
}
|
||||
}
|
||||
|
||||
func normalizeCompositeRouteMatchType(matchType string) string {
|
||||
matchType = strings.ToLower(strings.TrimSpace(matchType))
|
||||
switch matchType {
|
||||
case CompositeRouteMatchPrefix:
|
||||
return CompositeRouteMatchPrefix
|
||||
default:
|
||||
return CompositeRouteMatchExact
|
||||
}
|
||||
}
|
||||
|
||||
func normalizeCompositeRouteInput(input CompositeRouteInput) CompositeRouteInput {
|
||||
input.PublicModel = strings.TrimSpace(input.PublicModel)
|
||||
input.MatchType = normalizeCompositeRouteMatchType(input.MatchType)
|
||||
input.TargetPlatform = strings.TrimSpace(input.TargetPlatform)
|
||||
input.UpstreamModel = strings.TrimSpace(input.UpstreamModel)
|
||||
input.Endpoint = normalizeCompositeRouteEndpoint(input.Endpoint)
|
||||
if input.UpstreamModel == "" {
|
||||
input.UpstreamModel = input.PublicModel
|
||||
}
|
||||
input.Notes = strings.TrimSpace(input.Notes)
|
||||
return input
|
||||
}
|
||||
@@ -31,6 +31,59 @@ func ResolvedTargetPlatformFromContext(ctx context.Context) (string, bool) {
|
||||
return platform, true
|
||||
}
|
||||
|
||||
func WithCompositeRouteDecision(ctx context.Context, decision CompositeRouteDecision) context.Context {
|
||||
if ctx == nil || !decision.Matched {
|
||||
return ctx
|
||||
}
|
||||
ctx = WithResolvedTargetPlatform(ctx, decision.TargetPlatform)
|
||||
if model := strings.TrimSpace(decision.UpstreamModel); model != "" {
|
||||
ctx = context.WithValue(ctx, ctxkey.ResolvedUpstreamModel, model)
|
||||
}
|
||||
if model := strings.TrimSpace(decision.PublicModel); model != "" {
|
||||
ctx = context.WithValue(ctx, ctxkey.RequestedPublicModel, model)
|
||||
}
|
||||
if source := strings.TrimSpace(decision.Source); source != "" {
|
||||
ctx = context.WithValue(ctx, ctxkey.CompositeRouteSource, source)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
func ResolvedUpstreamModelFromContext(ctx context.Context) (string, bool) {
|
||||
if ctx == nil {
|
||||
return "", false
|
||||
}
|
||||
model, ok := ctx.Value(ctxkey.ResolvedUpstreamModel).(string)
|
||||
model = strings.TrimSpace(model)
|
||||
if !ok || model == "" {
|
||||
return "", false
|
||||
}
|
||||
return model, true
|
||||
}
|
||||
|
||||
func RequestedPublicModelFromContext(ctx context.Context) (string, bool) {
|
||||
if ctx == nil {
|
||||
return "", false
|
||||
}
|
||||
model, ok := ctx.Value(ctxkey.RequestedPublicModel).(string)
|
||||
model = strings.TrimSpace(model)
|
||||
if !ok || model == "" {
|
||||
return "", false
|
||||
}
|
||||
return model, true
|
||||
}
|
||||
|
||||
func CompositeRouteSourceFromContext(ctx context.Context) (string, bool) {
|
||||
if ctx == nil {
|
||||
return "", false
|
||||
}
|
||||
source, ok := ctx.Value(ctxkey.CompositeRouteSource).(string)
|
||||
source = strings.TrimSpace(source)
|
||||
if !ok || source == "" {
|
||||
return "", false
|
||||
}
|
||||
return source, true
|
||||
}
|
||||
|
||||
// DetectModelPlatform maps common public model IDs to the concrete provider
|
||||
// platform used by sub2api. It intentionally returns false for ambiguous model
|
||||
// names so composite groups fail closed instead of guessing.
|
||||
@@ -94,6 +147,36 @@ func hasOpenAISeriesPrefix(model string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *GatewayService) resolveCompositeRouteDecision(ctx context.Context, group *Group, requestedModel, endpoint string) (CompositeRouteDecision, bool, error) {
|
||||
if group == nil || group.Platform != PlatformComposite {
|
||||
return CompositeRouteDecision{}, false, nil
|
||||
}
|
||||
if platform, ok := ResolvedTargetPlatformFromContext(ctx); ok {
|
||||
upstreamModel := requestedModel
|
||||
if resolvedModel, modelOK := ResolvedUpstreamModelFromContext(ctx); modelOK {
|
||||
upstreamModel = resolvedModel
|
||||
}
|
||||
source := CompositeRouteSourceDetector
|
||||
if resolvedSource, sourceOK := CompositeRouteSourceFromContext(ctx); sourceOK {
|
||||
source = resolvedSource
|
||||
}
|
||||
return CompositeRouteDecision{
|
||||
Matched: true,
|
||||
Source: source,
|
||||
GroupID: group.ID,
|
||||
PublicModel: requestedModel,
|
||||
TargetPlatform: platform,
|
||||
UpstreamModel: upstreamModel,
|
||||
Endpoint: normalizeCompositeRouteEndpoint(endpoint),
|
||||
}, true, nil
|
||||
}
|
||||
decision, err := s.compositeResolver.Resolve(ctx, group.ID, requestedModel, endpoint)
|
||||
if err != nil {
|
||||
return decision, false, err
|
||||
}
|
||||
return decision, decision.Matched, nil
|
||||
}
|
||||
|
||||
func resolveCompositeTargetPlatform(ctx context.Context, group *Group, requestedModel string) (string, bool) {
|
||||
if platform, ok := ResolvedTargetPlatformFromContext(ctx); ok {
|
||||
return platform, true
|
||||
|
||||
@@ -49,10 +49,17 @@ func TestQuotaPlatformCompositeUsesResolvedOrForceOnly(t *testing.T) {
|
||||
require.Equal(t, PlatformAntigravity, QuotaPlatform(ctx, apiKey))
|
||||
}
|
||||
|
||||
func TestSchedulerPlatformsForCompositeGroup(t *testing.T) {
|
||||
func TestCompositeGroupSchedulerHasAllCanonicalPlatformBuckets(t *testing.T) {
|
||||
seen := make(map[string]struct{})
|
||||
for _, bucket := range schedulerCanonicalBuckets(99) {
|
||||
seen[bucket.Platform] = struct{}{}
|
||||
}
|
||||
platforms := make([]string, 0, len(seen))
|
||||
for platform := range seen {
|
||||
platforms = append(platforms, platform)
|
||||
}
|
||||
require.ElementsMatch(t,
|
||||
[]string{PlatformAnthropic, PlatformGemini, PlatformOpenAI, PlatformAntigravity, PlatformGrok},
|
||||
schedulerPlatformsForGroup(PlatformComposite),
|
||||
platforms,
|
||||
)
|
||||
require.Equal(t, []string{PlatformAnthropic}, schedulerPlatformsForGroup(PlatformAnthropic))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type CompositeRouteResolver struct {
|
||||
repo CompositeModelRouteRepository
|
||||
}
|
||||
|
||||
func NewCompositeRouteResolver(repo CompositeModelRouteRepository) *CompositeRouteResolver {
|
||||
return &CompositeRouteResolver{repo: repo}
|
||||
}
|
||||
|
||||
func (r *CompositeRouteResolver) Resolve(ctx context.Context, groupID int64, model, endpoint string) (CompositeRouteDecision, error) {
|
||||
model = strings.TrimSpace(model)
|
||||
endpoint = normalizeCompositeRouteEndpoint(endpoint)
|
||||
decision := CompositeRouteDecision{
|
||||
GroupID: groupID,
|
||||
PublicModel: model,
|
||||
Endpoint: endpoint,
|
||||
}
|
||||
if model == "" {
|
||||
decision.Reason = "model is required"
|
||||
return decision, nil
|
||||
}
|
||||
|
||||
if r != nil && r.repo != nil && groupID > 0 {
|
||||
routes, err := r.repo.ListByGroup(ctx, groupID, false)
|
||||
if err != nil {
|
||||
return decision, fmt.Errorf("list composite routes: %w", err)
|
||||
}
|
||||
if route, ok := matchCompositeRoute(routes, model, endpoint); ok {
|
||||
upstreamModel := strings.TrimSpace(route.UpstreamModel)
|
||||
if upstreamModel == "" {
|
||||
upstreamModel = model
|
||||
}
|
||||
return CompositeRouteDecision{
|
||||
Matched: true,
|
||||
Source: CompositeRouteSourceExplicit,
|
||||
GroupID: groupID,
|
||||
PublicModel: model,
|
||||
TargetPlatform: route.TargetPlatform,
|
||||
UpstreamModel: upstreamModel,
|
||||
Endpoint: endpoint,
|
||||
Route: &route,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
if platform, ok := DetectModelPlatform(model); ok {
|
||||
return CompositeRouteDecision{
|
||||
Matched: true,
|
||||
Source: CompositeRouteSourceDetector,
|
||||
GroupID: groupID,
|
||||
PublicModel: model,
|
||||
TargetPlatform: platform,
|
||||
UpstreamModel: model,
|
||||
Endpoint: endpoint,
|
||||
}, nil
|
||||
}
|
||||
decision.Reason = "no explicit route or built-in detector match"
|
||||
return decision, nil
|
||||
}
|
||||
|
||||
func matchCompositeRoute(routes []CompositeModelRoute, model, endpoint string) (CompositeModelRoute, bool) {
|
||||
if len(routes) == 0 {
|
||||
return CompositeModelRoute{}, false
|
||||
}
|
||||
|
||||
type candidate struct {
|
||||
route CompositeModelRoute
|
||||
matchStrength int
|
||||
endpointWeight int
|
||||
prefixLen int
|
||||
}
|
||||
candidates := make([]candidate, 0, len(routes))
|
||||
for _, route := range routes {
|
||||
route.Endpoint = normalizeCompositeRouteEndpoint(route.Endpoint)
|
||||
if route.Endpoint != endpoint && route.Endpoint != CompositeRouteEndpointAny {
|
||||
continue
|
||||
}
|
||||
route.MatchType = normalizeCompositeRouteMatchType(route.MatchType)
|
||||
publicModel := strings.TrimSpace(route.PublicModel)
|
||||
if publicModel == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
matchStrength := 0
|
||||
prefixLen := len(publicModel)
|
||||
switch route.MatchType {
|
||||
case CompositeRouteMatchExact:
|
||||
if publicModel != model {
|
||||
continue
|
||||
}
|
||||
matchStrength = 2
|
||||
case CompositeRouteMatchPrefix:
|
||||
if !strings.HasPrefix(model, publicModel) {
|
||||
continue
|
||||
}
|
||||
matchStrength = 1
|
||||
default:
|
||||
continue
|
||||
}
|
||||
endpointWeight := 0
|
||||
if route.Endpoint == endpoint {
|
||||
endpointWeight = 1
|
||||
}
|
||||
candidates = append(candidates, candidate{
|
||||
route: route,
|
||||
matchStrength: matchStrength,
|
||||
endpointWeight: endpointWeight,
|
||||
prefixLen: prefixLen,
|
||||
})
|
||||
}
|
||||
if len(candidates) == 0 {
|
||||
return CompositeModelRoute{}, false
|
||||
}
|
||||
|
||||
sort.SliceStable(candidates, func(i, j int) bool {
|
||||
a, b := candidates[i], candidates[j]
|
||||
if a.matchStrength != b.matchStrength {
|
||||
return a.matchStrength > b.matchStrength
|
||||
}
|
||||
if a.endpointWeight != b.endpointWeight {
|
||||
return a.endpointWeight > b.endpointWeight
|
||||
}
|
||||
if a.prefixLen != b.prefixLen {
|
||||
return a.prefixLen > b.prefixLen
|
||||
}
|
||||
if a.route.Priority != b.route.Priority {
|
||||
return a.route.Priority < b.route.Priority
|
||||
}
|
||||
return a.route.ID < b.route.ID
|
||||
})
|
||||
return candidates[0].route, true
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type compositeRouteRepoStub struct {
|
||||
routes []CompositeModelRoute
|
||||
}
|
||||
|
||||
func (s compositeRouteRepoStub) ListByGroup(ctx context.Context, groupID int64, includeDisabled bool) ([]CompositeModelRoute, error) {
|
||||
routes := make([]CompositeModelRoute, 0, len(s.routes))
|
||||
for _, route := range s.routes {
|
||||
if route.GroupID != groupID {
|
||||
continue
|
||||
}
|
||||
if !includeDisabled && !route.Enabled {
|
||||
continue
|
||||
}
|
||||
routes = append(routes, route)
|
||||
}
|
||||
return routes, nil
|
||||
}
|
||||
|
||||
func (s compositeRouteRepoStub) Create(ctx context.Context, route *CompositeModelRoute) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s compositeRouteRepoStub) Update(ctx context.Context, route *CompositeModelRoute) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s compositeRouteRepoStub) Delete(ctx context.Context, id int64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s compositeRouteRepoStub) DeleteByGroup(ctx context.Context, groupID int64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestCompositeRouteResolverExplicitExactRouteRewritesModel(t *testing.T) {
|
||||
resolver := NewCompositeRouteResolver(compositeRouteRepoStub{
|
||||
routes: []CompositeModelRoute{
|
||||
{
|
||||
ID: 10,
|
||||
GroupID: 7,
|
||||
PublicModel: "openrouter/gpt-5",
|
||||
MatchType: CompositeRouteMatchExact,
|
||||
TargetPlatform: PlatformOpenAI,
|
||||
UpstreamModel: "gpt-5",
|
||||
Endpoint: CompositeRouteEndpointAny,
|
||||
Priority: 100,
|
||||
Enabled: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
decision, err := resolver.Resolve(context.Background(), 7, "openrouter/gpt-5", CompositeRouteEndpointChatCompletions)
|
||||
|
||||
require.NoError(t, err)
|
||||
require.True(t, decision.Matched)
|
||||
require.Equal(t, CompositeRouteSourceExplicit, decision.Source)
|
||||
require.Equal(t, PlatformOpenAI, decision.TargetPlatform)
|
||||
require.Equal(t, "gpt-5", decision.UpstreamModel)
|
||||
require.NotNil(t, decision.Route)
|
||||
require.Equal(t, int64(10), decision.Route.ID)
|
||||
}
|
||||
|
||||
func TestCompositeRouteResolverPrefersEndpointSpecificLongestPrefix(t *testing.T) {
|
||||
resolver := NewCompositeRouteResolver(compositeRouteRepoStub{
|
||||
routes: []CompositeModelRoute{
|
||||
{
|
||||
ID: 1,
|
||||
GroupID: 7,
|
||||
PublicModel: "router/",
|
||||
MatchType: CompositeRouteMatchPrefix,
|
||||
TargetPlatform: PlatformAnthropic,
|
||||
Endpoint: CompositeRouteEndpointAny,
|
||||
Priority: 10,
|
||||
Enabled: true,
|
||||
},
|
||||
{
|
||||
ID: 2,
|
||||
GroupID: 7,
|
||||
PublicModel: "router/gpt-",
|
||||
MatchType: CompositeRouteMatchPrefix,
|
||||
TargetPlatform: PlatformOpenAI,
|
||||
UpstreamModel: "gpt-family",
|
||||
Endpoint: CompositeRouteEndpointResponses,
|
||||
Priority: 100,
|
||||
Enabled: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
decision, err := resolver.Resolve(context.Background(), 7, "router/gpt-5", CompositeRouteEndpointResponses)
|
||||
|
||||
require.NoError(t, err)
|
||||
require.True(t, decision.Matched)
|
||||
require.Equal(t, CompositeRouteSourceExplicit, decision.Source)
|
||||
require.Equal(t, PlatformOpenAI, decision.TargetPlatform)
|
||||
require.Equal(t, "gpt-family", decision.UpstreamModel)
|
||||
require.NotNil(t, decision.Route)
|
||||
require.Equal(t, int64(2), decision.Route.ID)
|
||||
}
|
||||
|
||||
func TestCompositeRouteResolverIgnoresDisabledRoutesAndFallsBackToDetector(t *testing.T) {
|
||||
resolver := NewCompositeRouteResolver(compositeRouteRepoStub{
|
||||
routes: []CompositeModelRoute{
|
||||
{
|
||||
ID: 1,
|
||||
GroupID: 7,
|
||||
PublicModel: "gpt-5",
|
||||
MatchType: CompositeRouteMatchExact,
|
||||
TargetPlatform: PlatformAnthropic,
|
||||
UpstreamModel: "claude-sonnet-4-6",
|
||||
Endpoint: CompositeRouteEndpointAny,
|
||||
Priority: 100,
|
||||
Enabled: false,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
decision, err := resolver.Resolve(context.Background(), 7, "gpt-5", CompositeRouteEndpointAny)
|
||||
|
||||
require.NoError(t, err)
|
||||
require.True(t, decision.Matched)
|
||||
require.Equal(t, CompositeRouteSourceDetector, decision.Source)
|
||||
require.Equal(t, PlatformOpenAI, decision.TargetPlatform)
|
||||
require.Equal(t, "gpt-5", decision.UpstreamModel)
|
||||
require.Nil(t, decision.Route)
|
||||
}
|
||||
@@ -44,6 +44,7 @@ func newGatewayRecordUsageServiceForTest(usageRepo UsageLogRepository, userRepo
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil, // userPlatformQuotaRepo
|
||||
)
|
||||
}
|
||||
|
||||
@@ -46,12 +46,16 @@ func (s *GatewayService) SelectAccountForModelWithExclusions(ctx context.Context
|
||||
ctx = s.withGroupContext(ctx, group)
|
||||
platform = group.Platform
|
||||
if group != nil && group.Platform == PlatformComposite {
|
||||
targetPlatform, ok := resolveCompositeTargetPlatform(ctx, group, requestedModel)
|
||||
decision, ok, err := s.resolveCompositeRouteDecision(ctx, group, requestedModel, CompositeRouteEndpointAny)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%w supporting model: %s (composite target platform unknown)", ErrNoAvailableAccounts, requestedModel)
|
||||
}
|
||||
platform = targetPlatform
|
||||
ctx = WithResolvedTargetPlatform(ctx, targetPlatform)
|
||||
platform = decision.TargetPlatform
|
||||
requestedModel = decision.UpstreamModel
|
||||
ctx = WithCompositeRouteDecision(ctx, decision)
|
||||
}
|
||||
} else {
|
||||
// 无分组时只使用原生 anthropic 平台
|
||||
@@ -908,11 +912,14 @@ func (s *GatewayService) resolvePlatform(ctx context.Context, groupID *int64, gr
|
||||
}
|
||||
if group != nil {
|
||||
if group.Platform == PlatformComposite {
|
||||
targetPlatform, ok := resolveCompositeTargetPlatform(ctx, group, requestedModel)
|
||||
decision, ok, err := s.resolveCompositeRouteDecision(ctx, group, requestedModel, CompositeRouteEndpointAny)
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
if !ok {
|
||||
return "", false, fmt.Errorf("%w supporting model: %s (composite target platform unknown)", ErrNoAvailableAccounts, requestedModel)
|
||||
}
|
||||
return targetPlatform, false, nil
|
||||
return decision.TargetPlatform, false, nil
|
||||
}
|
||||
return group.Platform, false, nil
|
||||
}
|
||||
@@ -922,11 +929,14 @@ func (s *GatewayService) resolvePlatform(ctx context.Context, groupID *int64, gr
|
||||
return "", false, err
|
||||
}
|
||||
if group.Platform == PlatformComposite {
|
||||
targetPlatform, ok := resolveCompositeTargetPlatform(ctx, group, requestedModel)
|
||||
decision, ok, err := s.resolveCompositeRouteDecision(ctx, group, requestedModel, CompositeRouteEndpointAny)
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
if !ok {
|
||||
return "", false, fmt.Errorf("%w supporting model: %s (composite target platform unknown)", ErrNoAvailableAccounts, requestedModel)
|
||||
}
|
||||
return targetPlatform, false, nil
|
||||
return decision.TargetPlatform, false, nil
|
||||
}
|
||||
return group.Platform, false, nil
|
||||
}
|
||||
|
||||
@@ -708,6 +708,7 @@ type GatewayService struct {
|
||||
debugClaudeMimic atomic.Bool
|
||||
channelService *ChannelService
|
||||
resolver *ModelPricingResolver
|
||||
compositeResolver *CompositeRouteResolver
|
||||
debugGatewayBodyFile atomic.Pointer[os.File] // non-nil when SUB2API_DEBUG_GATEWAY_BODY is set
|
||||
tlsFPProfileService *TLSFingerprintProfileService
|
||||
balanceNotifyService *BalanceNotifyService
|
||||
@@ -741,6 +742,7 @@ func NewGatewayService(
|
||||
tlsFPProfileService *TLSFingerprintProfileService,
|
||||
channelService *ChannelService,
|
||||
resolver *ModelPricingResolver,
|
||||
compositeResolver *CompositeRouteResolver,
|
||||
balanceNotifyService *BalanceNotifyService,
|
||||
userPlatformQuotaRepo UserPlatformQuotaRepository,
|
||||
) *GatewayService {
|
||||
@@ -777,6 +779,7 @@ func NewGatewayService(
|
||||
tlsFPProfileService: tlsFPProfileService,
|
||||
channelService: channelService,
|
||||
resolver: resolver,
|
||||
compositeResolver: compositeResolver,
|
||||
balanceNotifyService: balanceNotifyService,
|
||||
userPlatformQuotaRepo: userPlatformQuotaRepo,
|
||||
}
|
||||
|
||||
@@ -681,6 +681,7 @@ var ProviderSet = wire.NewSet(
|
||||
ProvideAPIKeyAuthCacheInvalidator,
|
||||
ProvideAuthCacheInvalidationWorker,
|
||||
NewGroupService,
|
||||
NewCompositeRouteResolver,
|
||||
NewAccountService,
|
||||
NewProxyService,
|
||||
NewRedeemService,
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
CREATE TABLE IF NOT EXISTS composite_model_routes (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
group_id BIGINT NOT NULL REFERENCES groups(id) ON DELETE CASCADE,
|
||||
public_model VARCHAR(200) NOT NULL,
|
||||
match_type VARCHAR(20) NOT NULL DEFAULT 'exact',
|
||||
target_platform VARCHAR(50) NOT NULL,
|
||||
upstream_model VARCHAR(200) NOT NULL DEFAULT '',
|
||||
endpoint VARCHAR(50) NOT NULL DEFAULT 'any',
|
||||
priority INTEGER NOT NULL DEFAULT 100,
|
||||
enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
notes TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
deleted_at TIMESTAMPTZ NULL,
|
||||
CONSTRAINT composite_model_routes_match_type_check CHECK (match_type IN ('exact', 'prefix')),
|
||||
CONSTRAINT composite_model_routes_endpoint_check CHECK (endpoint IN ('any', 'messages', 'count_tokens', 'responses', 'chat_completions', 'embeddings', 'images', 'gemini')),
|
||||
CONSTRAINT composite_model_routes_target_platform_check CHECK (target_platform IN ('anthropic', 'openai', 'gemini', 'antigravity', 'grok'))
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_composite_model_routes_unique_active
|
||||
ON composite_model_routes (group_id, endpoint, match_type, public_model)
|
||||
WHERE deleted_at IS NULL;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_composite_model_routes_group_enabled
|
||||
ON composite_model_routes (group_id, enabled)
|
||||
WHERE deleted_at IS NULL;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_composite_model_routes_group_priority
|
||||
ON composite_model_routes (group_id, priority, id)
|
||||
WHERE deleted_at IS NULL;
|
||||
@@ -2,7 +2,8 @@
|
||||
|
||||
Composite groups are an admin routing layer for API keys that should choose a
|
||||
concrete provider from the requested model instead of binding the key to a
|
||||
single provider group.
|
||||
single provider group. They support both built-in model detection and an
|
||||
admin-configured model route registry for public model aliases.
|
||||
|
||||
## Supported Providers
|
||||
|
||||
@@ -18,7 +19,41 @@ The selected concrete platform is used for account selection, user platform
|
||||
quota checks, post-usage billing, ops error platform attribution, channel
|
||||
mapping/pricing lookup, and platform usage reporting.
|
||||
|
||||
## Model Detection
|
||||
## Route Registry
|
||||
|
||||
Admins can configure routes on a composite group from the group list's
|
||||
`Routes` action or through the admin API:
|
||||
|
||||
- `GET /api/v1/admin/groups/:id/composite-routes`
|
||||
- `POST /api/v1/admin/groups/:id/composite-routes`
|
||||
- `PUT /api/v1/admin/groups/:id/composite-routes/:route_id`
|
||||
- `DELETE /api/v1/admin/groups/:id/composite-routes/:route_id`
|
||||
- `POST /api/v1/admin/groups/:id/composite-routes/preview`
|
||||
|
||||
Each route belongs to one composite group and contains:
|
||||
|
||||
- `public_model`: model identifier the client sends.
|
||||
- `match_type`: `exact` or `prefix`.
|
||||
- `target_platform`: concrete provider platform.
|
||||
- `upstream_model`: model identifier sent upstream. If omitted, the public
|
||||
model is reused.
|
||||
- `endpoint`: `any`, `messages`, `count_tokens`, `responses`,
|
||||
`chat_completions`, `embeddings`, `images`, or `gemini`.
|
||||
- `priority`: lower values win after match specificity.
|
||||
- `enabled`: disabled routes are ignored by runtime resolution but remain
|
||||
visible to admins.
|
||||
|
||||
Resolution order is explicit route first, then built-in detection. When more
|
||||
than one explicit route matches, exact matches beat prefix matches,
|
||||
endpoint-specific routes beat `any`, longer prefixes beat shorter prefixes,
|
||||
then lower `priority`, then lower route id.
|
||||
|
||||
For JSON-body endpoints, the gateway rewrites the request `model` field to the
|
||||
route's `upstream_model` before dispatch. For Gemini native paths such as
|
||||
`/v1beta/models/{model}:generateContent`, the gateway resolves `{model}` and
|
||||
the handler forwards the resolved upstream model.
|
||||
|
||||
## Built-In Detection
|
||||
|
||||
Composite routing detects common public model IDs and provider-prefixed IDs:
|
||||
|
||||
@@ -34,6 +69,7 @@ guessing a provider.
|
||||
## Admin Workflows
|
||||
|
||||
- Admins can create a group with platform `composite`.
|
||||
- Admins can add, edit, delete, and preview composite model routes.
|
||||
- Composite groups can copy accounts from concrete provider groups.
|
||||
- Concrete provider accounts can be assigned directly to composite groups from
|
||||
account create/edit and bulk account workflows.
|
||||
@@ -43,7 +79,7 @@ guessing a provider.
|
||||
|
||||
## Limits
|
||||
|
||||
Composite groups are not a full OpenRouter-compatible model registry. They do
|
||||
not add a provider/model mapping database, per-model admin routing overrides, or
|
||||
arbitrary third-party provider prefixes. Add those explicitly before relying on
|
||||
custom model IDs that cannot be detected from their names.
|
||||
Composite routes choose a concrete provider and upstream model; they do not
|
||||
create synthetic model metadata, pricing, or upstream capability records by
|
||||
themselves. Keep channel pricing/model mapping configured for the concrete
|
||||
provider platforms that the routes target.
|
||||
|
||||
@@ -7,6 +7,10 @@ import { apiClient } from '../client'
|
||||
import type {
|
||||
AdminGroup,
|
||||
GroupPlatform,
|
||||
CompositeModelRoute,
|
||||
CompositeModelRouteInput,
|
||||
CompositeRoutePreviewRequest,
|
||||
CompositeRouteDecision,
|
||||
CreateGroupRequest,
|
||||
UpdateGroupRequest,
|
||||
PaginatedResponse
|
||||
@@ -263,6 +267,55 @@ export async function getGroupApiKeys(
|
||||
return data
|
||||
}
|
||||
|
||||
export async function listCompositeRoutes(id: number): Promise<CompositeModelRoute[]> {
|
||||
const { data } = await apiClient.get<CompositeModelRoute[]>(`/admin/groups/${id}/composite-routes`)
|
||||
return data
|
||||
}
|
||||
|
||||
export async function createCompositeRoute(
|
||||
id: number,
|
||||
route: CompositeModelRouteInput
|
||||
): Promise<CompositeModelRoute> {
|
||||
const { data } = await apiClient.post<CompositeModelRoute>(
|
||||
`/admin/groups/${id}/composite-routes`,
|
||||
route
|
||||
)
|
||||
return data
|
||||
}
|
||||
|
||||
export async function updateCompositeRoute(
|
||||
id: number,
|
||||
routeId: number,
|
||||
route: CompositeModelRouteInput
|
||||
): Promise<CompositeModelRoute> {
|
||||
const { data } = await apiClient.put<CompositeModelRoute>(
|
||||
`/admin/groups/${id}/composite-routes/${routeId}`,
|
||||
route
|
||||
)
|
||||
return data
|
||||
}
|
||||
|
||||
export async function deleteCompositeRoute(
|
||||
id: number,
|
||||
routeId: number
|
||||
): Promise<{ message: string }> {
|
||||
const { data } = await apiClient.delete<{ message: string }>(
|
||||
`/admin/groups/${id}/composite-routes/${routeId}`
|
||||
)
|
||||
return data
|
||||
}
|
||||
|
||||
export async function previewCompositeRoute(
|
||||
id: number,
|
||||
request: CompositeRoutePreviewRequest
|
||||
): Promise<CompositeRouteDecision> {
|
||||
const { data } = await apiClient.post<CompositeRouteDecision>(
|
||||
`/admin/groups/${id}/composite-routes/preview`,
|
||||
request
|
||||
)
|
||||
return data
|
||||
}
|
||||
|
||||
/**
|
||||
* Rate multiplier entry for a user in a group
|
||||
*/
|
||||
@@ -423,6 +476,11 @@ export const groupsAPI = {
|
||||
toggleStatus,
|
||||
getStats,
|
||||
getGroupApiKeys,
|
||||
listCompositeRoutes,
|
||||
createCompositeRoute,
|
||||
updateCompositeRoute,
|
||||
deleteCompositeRoute,
|
||||
previewCompositeRoute,
|
||||
getGroupRateMultipliers,
|
||||
clearGroupRateMultipliers,
|
||||
batchSetGroupRateMultipliers,
|
||||
|
||||
@@ -1015,6 +1015,55 @@ export default {
|
||||
selectAll: 'Select all',
|
||||
invertSelection: 'Invert'
|
||||
},
|
||||
compositeRoutes: {
|
||||
action: 'Routes',
|
||||
title: 'Composite Routes',
|
||||
titleWithGroup: 'Composite Routes: {name}',
|
||||
routes: 'Saved Routes',
|
||||
empty: 'No composite routes configured',
|
||||
publicModel: 'Public Model',
|
||||
target: 'Target',
|
||||
scope: 'Scope',
|
||||
priority: 'Priority',
|
||||
addRoute: 'Add Route',
|
||||
editRoute: 'Edit Route',
|
||||
matchType: 'Match',
|
||||
endpoint: 'Endpoint',
|
||||
targetPlatform: 'Target Platform',
|
||||
upstreamModel: 'Upstream Model',
|
||||
notes: 'Notes',
|
||||
enabled: 'Enabled',
|
||||
preview: 'Preview',
|
||||
matched: 'Matched',
|
||||
notMatched: 'No Match',
|
||||
publicModelRequired: 'Public model is required',
|
||||
routeCreated: 'Composite route created',
|
||||
routeUpdated: 'Composite route updated',
|
||||
routeDeleted: 'Composite route deleted',
|
||||
failedToLoad: 'Failed to load composite routes',
|
||||
failedToSave: 'Failed to save composite route',
|
||||
failedToDelete: 'Failed to delete composite route',
|
||||
failedToPreview: 'Failed to preview composite route',
|
||||
deleteConfirm: 'Delete this composite route?',
|
||||
endpoints: {
|
||||
any: 'Any',
|
||||
messages: 'Messages',
|
||||
countTokens: 'Count Tokens',
|
||||
responses: 'Responses',
|
||||
chatCompletions: 'Chat Completions',
|
||||
embeddings: 'Embeddings',
|
||||
images: 'Images',
|
||||
gemini: 'Gemini Native'
|
||||
},
|
||||
match: {
|
||||
exact: 'Exact',
|
||||
prefix: 'Prefix'
|
||||
},
|
||||
sources: {
|
||||
route: 'Route',
|
||||
detector: 'Detector'
|
||||
}
|
||||
},
|
||||
claudeCode: {
|
||||
title: 'Claude Code Client Restriction',
|
||||
tooltip: 'When enabled, this group only allows official Claude Code clients. Non-Claude Code requests will be rejected or fallback to the specified group.',
|
||||
|
||||
@@ -1012,6 +1012,55 @@ export default {
|
||||
selectAll: '全选',
|
||||
invertSelection: '反选'
|
||||
},
|
||||
compositeRoutes: {
|
||||
action: '路由',
|
||||
title: 'Composite 路由',
|
||||
titleWithGroup: 'Composite 路由:{name}',
|
||||
routes: '已保存路由',
|
||||
empty: '暂无 Composite 路由',
|
||||
publicModel: '公开模型',
|
||||
target: '目标',
|
||||
scope: '范围',
|
||||
priority: '优先级',
|
||||
addRoute: '添加路由',
|
||||
editRoute: '编辑路由',
|
||||
matchType: '匹配方式',
|
||||
endpoint: '端点',
|
||||
targetPlatform: '目标平台',
|
||||
upstreamModel: '上游模型',
|
||||
notes: '备注',
|
||||
enabled: '启用',
|
||||
preview: '预览',
|
||||
matched: '已匹配',
|
||||
notMatched: '未匹配',
|
||||
publicModelRequired: '请输入公开模型',
|
||||
routeCreated: 'Composite 路由已创建',
|
||||
routeUpdated: 'Composite 路由已更新',
|
||||
routeDeleted: 'Composite 路由已删除',
|
||||
failedToLoad: '加载 Composite 路由失败',
|
||||
failedToSave: '保存 Composite 路由失败',
|
||||
failedToDelete: '删除 Composite 路由失败',
|
||||
failedToPreview: '预览 Composite 路由失败',
|
||||
deleteConfirm: '确定删除此 Composite 路由?',
|
||||
endpoints: {
|
||||
any: '任意',
|
||||
messages: 'Messages',
|
||||
countTokens: 'Count Tokens',
|
||||
responses: 'Responses',
|
||||
chatCompletions: 'Chat Completions',
|
||||
embeddings: 'Embeddings',
|
||||
images: 'Images',
|
||||
gemini: 'Gemini 原生'
|
||||
},
|
||||
match: {
|
||||
exact: '精确',
|
||||
prefix: '前缀'
|
||||
},
|
||||
sources: {
|
||||
route: '路由',
|
||||
detector: '内置识别'
|
||||
}
|
||||
},
|
||||
claudeCode: {
|
||||
title: 'Claude Code 客户端限制',
|
||||
tooltip:
|
||||
|
||||
@@ -589,6 +589,63 @@ export interface ModelsListConfig {
|
||||
models: string[]
|
||||
}
|
||||
|
||||
export type CompositeRouteMatchType = 'exact' | 'prefix'
|
||||
|
||||
export type CompositeRouteEndpoint =
|
||||
| 'any'
|
||||
| 'messages'
|
||||
| 'count_tokens'
|
||||
| 'responses'
|
||||
| 'chat_completions'
|
||||
| 'embeddings'
|
||||
| 'images'
|
||||
| 'gemini'
|
||||
|
||||
export type CompositeRouteSource = 'route' | 'detector' | string
|
||||
|
||||
export interface CompositeModelRoute {
|
||||
id: number
|
||||
group_id: number
|
||||
public_model: string
|
||||
match_type: CompositeRouteMatchType
|
||||
target_platform: Exclude<GroupPlatform, 'composite'>
|
||||
upstream_model: string
|
||||
endpoint: CompositeRouteEndpoint
|
||||
priority: number
|
||||
enabled: boolean
|
||||
notes: string
|
||||
created_at?: string
|
||||
updated_at?: string
|
||||
}
|
||||
|
||||
export interface CompositeModelRouteInput {
|
||||
public_model: string
|
||||
match_type: CompositeRouteMatchType
|
||||
target_platform: Exclude<GroupPlatform, 'composite'>
|
||||
upstream_model?: string
|
||||
endpoint: CompositeRouteEndpoint
|
||||
priority?: number
|
||||
enabled?: boolean
|
||||
notes?: string
|
||||
}
|
||||
|
||||
export interface CompositeRoutePreviewRequest {
|
||||
model: string
|
||||
endpoint: CompositeRouteEndpoint
|
||||
}
|
||||
|
||||
export interface CompositeRouteDecision {
|
||||
matched: boolean
|
||||
source: CompositeRouteSource
|
||||
group_id: number
|
||||
public_model: string
|
||||
target_platform: Exclude<GroupPlatform, 'composite'> | ''
|
||||
upstream_model: string
|
||||
endpoint: CompositeRouteEndpoint
|
||||
route?: CompositeModelRoute
|
||||
reason?: string
|
||||
}
|
||||
|
||||
export interface ApiKey {
|
||||
id: number
|
||||
user_id: number
|
||||
|
||||
@@ -386,6 +386,16 @@
|
||||
}}
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
v-if="row.platform === 'composite'"
|
||||
@click="handleCompositeRoutes(row)"
|
||||
class="flex flex-col items-center gap-0.5 rounded-lg p-1.5 text-gray-500 transition-colors hover:bg-gray-100 hover:text-cyan-600 dark:hover:bg-dark-700 dark:hover:text-cyan-400"
|
||||
>
|
||||
<Icon name="swap" size="sm" />
|
||||
<span class="text-xs">{{
|
||||
t("admin.groups.compositeRoutes.action")
|
||||
}}</span>
|
||||
</button>
|
||||
<button
|
||||
@click="handleRateMultipliers(row)"
|
||||
class="flex flex-col items-center gap-0.5 rounded-lg p-1.5 text-gray-500 transition-colors hover:bg-gray-100 hover:text-purple-600 dark:hover:bg-dark-700 dark:hover:text-purple-400"
|
||||
@@ -3578,6 +3588,362 @@
|
||||
</template>
|
||||
</BaseDialog>
|
||||
|
||||
<!-- Composite Routes Modal -->
|
||||
<BaseDialog
|
||||
:show="showCompositeRoutesModal"
|
||||
:title="
|
||||
compositeRoutesGroup
|
||||
? t('admin.groups.compositeRoutes.titleWithGroup', {
|
||||
name: compositeRoutesGroup.name,
|
||||
})
|
||||
: t('admin.groups.compositeRoutes.title')
|
||||
"
|
||||
width="wide"
|
||||
@close="closeCompositeRoutesModal"
|
||||
>
|
||||
<div class="grid gap-5 lg:grid-cols-[minmax(0,1.15fr)_minmax(320px,0.85fr)]">
|
||||
<section class="min-w-0">
|
||||
<div class="mb-3 flex items-center justify-between gap-3">
|
||||
<h3 class="text-sm font-semibold text-gray-900 dark:text-white">
|
||||
{{ t("admin.groups.compositeRoutes.routes") }}
|
||||
</h3>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-secondary btn-sm"
|
||||
:disabled="compositeRoutesLoading"
|
||||
@click="loadCompositeRoutes"
|
||||
>
|
||||
<Icon
|
||||
name="refresh"
|
||||
size="sm"
|
||||
:class="compositeRoutesLoading ? 'animate-spin' : ''"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="overflow-hidden rounded-lg border border-gray-200 dark:border-dark-600"
|
||||
>
|
||||
<div
|
||||
v-if="compositeRoutesLoading"
|
||||
class="flex h-36 items-center justify-center text-sm text-gray-500 dark:text-gray-400"
|
||||
>
|
||||
{{ t("common.loading") }}
|
||||
</div>
|
||||
<div
|
||||
v-else-if="compositeRoutes.length === 0"
|
||||
class="flex h-36 items-center justify-center text-sm text-gray-500 dark:text-gray-400"
|
||||
>
|
||||
{{ t("admin.groups.compositeRoutes.empty") }}
|
||||
</div>
|
||||
<div v-else class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-gray-200 text-sm dark:divide-dark-600">
|
||||
<thead class="bg-gray-50 text-left text-xs font-medium uppercase tracking-wide text-gray-500 dark:bg-dark-800 dark:text-gray-400">
|
||||
<tr>
|
||||
<th class="px-3 py-2">
|
||||
{{ t("admin.groups.compositeRoutes.publicModel") }}
|
||||
</th>
|
||||
<th class="px-3 py-2">
|
||||
{{ t("admin.groups.compositeRoutes.target") }}
|
||||
</th>
|
||||
<th class="px-3 py-2">
|
||||
{{ t("admin.groups.compositeRoutes.scope") }}
|
||||
</th>
|
||||
<th class="px-3 py-2 text-right">
|
||||
{{ t("admin.groups.columns.actions") }}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-100 bg-white dark:divide-dark-700 dark:bg-dark-900">
|
||||
<tr
|
||||
v-for="route in compositeRoutes"
|
||||
:key="route.id"
|
||||
:class="!route.enabled && 'opacity-60'"
|
||||
>
|
||||
<td class="max-w-[15rem] px-3 py-2">
|
||||
<div class="break-all font-medium text-gray-900 dark:text-white">
|
||||
{{ route.public_model }}
|
||||
</div>
|
||||
<div class="mt-1 flex flex-wrap items-center gap-1.5">
|
||||
<span class="badge badge-gray">{{
|
||||
compositeRouteMatchLabel(route.match_type)
|
||||
}}</span>
|
||||
<span
|
||||
v-if="!route.enabled"
|
||||
class="badge badge-danger"
|
||||
>
|
||||
{{ t("admin.accounts.status.inactive") }}
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-3 py-2">
|
||||
<div class="flex items-center gap-1.5 text-gray-900 dark:text-white">
|
||||
<PlatformIcon :platform="route.target_platform" size="xs" />
|
||||
<span>{{ formatCompositePlatform(route.target_platform) }}</span>
|
||||
</div>
|
||||
<div class="mt-1 break-all text-xs text-gray-500 dark:text-gray-400">
|
||||
{{ route.upstream_model || route.public_model }}
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-3 py-2">
|
||||
<div class="text-gray-700 dark:text-gray-300">
|
||||
{{ formatCompositeEndpoint(route.endpoint) }}
|
||||
</div>
|
||||
<div class="text-xs text-gray-500 dark:text-gray-400">
|
||||
{{ t("admin.groups.compositeRoutes.priority") }}:
|
||||
{{ route.priority }}
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-3 py-2">
|
||||
<div class="flex justify-end gap-1">
|
||||
<button
|
||||
type="button"
|
||||
class="rounded p-1.5 text-gray-500 hover:bg-gray-100 hover:text-primary-600 dark:hover:bg-dark-700 dark:hover:text-primary-400"
|
||||
:title="t('common.edit')"
|
||||
@click="editCompositeRoute(route)"
|
||||
>
|
||||
<Icon name="edit" size="sm" />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="rounded p-1.5 text-gray-500 hover:bg-red-50 hover:text-red-600 dark:hover:bg-red-900/20 dark:hover:text-red-400"
|
||||
:title="t('common.delete')"
|
||||
@click="deleteCompositeRoute(route)"
|
||||
>
|
||||
<Icon name="trash" size="sm" />
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="space-y-5">
|
||||
<form class="space-y-3" @submit.prevent="saveCompositeRoute">
|
||||
<div class="flex items-center justify-between gap-3">
|
||||
<h3 class="text-sm font-semibold text-gray-900 dark:text-white">
|
||||
{{
|
||||
compositeRouteEditingId
|
||||
? t("admin.groups.compositeRoutes.editRoute")
|
||||
: t("admin.groups.compositeRoutes.addRoute")
|
||||
}}
|
||||
</h3>
|
||||
<button
|
||||
v-if="compositeRouteEditingId"
|
||||
type="button"
|
||||
class="text-xs font-medium text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
|
||||
@click="resetCompositeRouteForm"
|
||||
>
|
||||
{{ t("common.cancel") }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="input-label">{{
|
||||
t("admin.groups.compositeRoutes.publicModel")
|
||||
}}</label>
|
||||
<input
|
||||
v-model.trim="compositeRouteForm.public_model"
|
||||
type="text"
|
||||
class="input"
|
||||
required
|
||||
placeholder="openrouter/gpt-5"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 gap-3 sm:grid-cols-2">
|
||||
<div>
|
||||
<label class="input-label">{{
|
||||
t("admin.groups.compositeRoutes.matchType")
|
||||
}}</label>
|
||||
<Select
|
||||
v-model="compositeRouteForm.match_type"
|
||||
:options="compositeRouteMatchOptions"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label class="input-label">{{
|
||||
t("admin.groups.compositeRoutes.endpoint")
|
||||
}}</label>
|
||||
<Select
|
||||
v-model="compositeRouteForm.endpoint"
|
||||
:options="compositeRouteEndpointOptions"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 gap-3 sm:grid-cols-2">
|
||||
<div>
|
||||
<label class="input-label">{{
|
||||
t("admin.groups.compositeRoutes.targetPlatform")
|
||||
}}</label>
|
||||
<Select
|
||||
v-model="compositeRouteForm.target_platform"
|
||||
:options="compositeRoutePlatformOptions"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label class="input-label">{{
|
||||
t("admin.groups.compositeRoutes.priority")
|
||||
}}</label>
|
||||
<input
|
||||
v-model.number="compositeRouteForm.priority"
|
||||
type="number"
|
||||
min="1"
|
||||
step="1"
|
||||
class="input"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="input-label">{{
|
||||
t("admin.groups.compositeRoutes.upstreamModel")
|
||||
}}</label>
|
||||
<input
|
||||
v-model.trim="compositeRouteForm.upstream_model"
|
||||
type="text"
|
||||
class="input"
|
||||
placeholder="gpt-5"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="input-label">{{
|
||||
t("admin.groups.compositeRoutes.notes")
|
||||
}}</label>
|
||||
<textarea
|
||||
v-model.trim="compositeRouteForm.notes"
|
||||
rows="2"
|
||||
class="input"
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between gap-3">
|
||||
<label class="flex items-center gap-2 text-sm text-gray-700 dark:text-gray-300">
|
||||
<input
|
||||
v-model="compositeRouteForm.enabled"
|
||||
type="checkbox"
|
||||
class="h-4 w-4 rounded border-gray-300 text-primary-600 focus:ring-primary-500 dark:border-dark-600 dark:bg-dark-700"
|
||||
/>
|
||||
{{ t("admin.groups.compositeRoutes.enabled") }}
|
||||
</label>
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-primary"
|
||||
:disabled="compositeRouteSaving"
|
||||
>
|
||||
<Icon
|
||||
v-if="!compositeRouteSaving"
|
||||
name="check"
|
||||
size="sm"
|
||||
class="mr-2"
|
||||
/>
|
||||
{{ compositeRouteEditingId ? t("common.update") : t("common.create") }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="border-t border-gray-200 pt-4 dark:border-dark-600">
|
||||
<h3 class="mb-3 text-sm font-semibold text-gray-900 dark:text-white">
|
||||
{{ t("admin.groups.compositeRoutes.preview") }}
|
||||
</h3>
|
||||
<div class="space-y-3">
|
||||
<input
|
||||
v-model.trim="compositePreviewModel"
|
||||
type="text"
|
||||
class="input"
|
||||
placeholder="openrouter/gpt-5"
|
||||
@keyup.enter="previewCompositeRoute"
|
||||
/>
|
||||
<div class="flex gap-2">
|
||||
<Select
|
||||
v-model="compositePreviewEndpoint"
|
||||
:options="compositeRouteEndpointOptions"
|
||||
class="min-w-0 flex-1"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-secondary"
|
||||
:disabled="compositePreviewLoading || !compositePreviewModel"
|
||||
@click="previewCompositeRoute"
|
||||
>
|
||||
<Icon name="play" size="sm" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="compositePreviewDecision"
|
||||
class="rounded-lg border border-gray-200 bg-gray-50 p-3 text-sm dark:border-dark-600 dark:bg-dark-800"
|
||||
>
|
||||
<div class="mb-2 flex items-center gap-2">
|
||||
<span
|
||||
:class="[
|
||||
'badge',
|
||||
compositePreviewDecision.matched
|
||||
? 'badge-success'
|
||||
: 'badge-danger',
|
||||
]"
|
||||
>
|
||||
{{
|
||||
compositePreviewDecision.matched
|
||||
? t("admin.groups.compositeRoutes.matched")
|
||||
: t("admin.groups.compositeRoutes.notMatched")
|
||||
}}
|
||||
</span>
|
||||
<span class="badge badge-gray">
|
||||
{{
|
||||
compositeRouteSourceLabel(
|
||||
compositePreviewDecision.source,
|
||||
)
|
||||
}}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
v-if="compositePreviewDecision.matched"
|
||||
class="space-y-1 text-gray-700 dark:text-gray-300"
|
||||
>
|
||||
<div>
|
||||
{{ t("admin.groups.compositeRoutes.targetPlatform") }}:
|
||||
{{
|
||||
formatCompositePlatform(
|
||||
compositePreviewDecision.target_platform,
|
||||
)
|
||||
}}
|
||||
</div>
|
||||
<div class="break-all">
|
||||
{{ t("admin.groups.compositeRoutes.upstreamModel") }}:
|
||||
{{ compositePreviewDecision.upstream_model }}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="text-gray-500 dark:text-gray-400"
|
||||
>
|
||||
{{ compositePreviewDecision.reason }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<div class="flex justify-end pt-4">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-secondary"
|
||||
@click="closeCompositeRoutesModal"
|
||||
>
|
||||
{{ t("common.close") }}
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
</BaseDialog>
|
||||
|
||||
<!-- Group Rate Multipliers Modal -->
|
||||
<GroupRateMultipliersModal
|
||||
:show="showRateMultipliersModal"
|
||||
@@ -3602,7 +3968,16 @@ import { useI18n } from "vue-i18n";
|
||||
import { useAppStore } from "@/stores/app";
|
||||
import { useOnboardingStore } from "@/stores/onboarding";
|
||||
import { adminAPI } from "@/api/admin";
|
||||
import type { AdminGroup, GroupPlatform, SubscriptionType } from "@/types";
|
||||
import type {
|
||||
AdminGroup,
|
||||
CompositeModelRoute,
|
||||
CompositeModelRouteInput,
|
||||
CompositeRouteDecision,
|
||||
CompositeRouteEndpoint,
|
||||
CompositeRouteMatchType,
|
||||
GroupPlatform,
|
||||
SubscriptionType,
|
||||
} from "@/types";
|
||||
import type { Column } from "@/components/common/types";
|
||||
import AppLayout from "@/components/layout/AppLayout.vue";
|
||||
import TablePageLayout from "@/components/layout/TablePageLayout.vue";
|
||||
@@ -3854,6 +4229,45 @@ const platformFilterOptions = computed(() => [
|
||||
{ value: "composite", label: "Composite" },
|
||||
]);
|
||||
|
||||
const compositeRoutePlatformOptions = computed(() => [
|
||||
{ value: "anthropic", label: "Anthropic" },
|
||||
{ value: "openai", label: "OpenAI" },
|
||||
{ value: "gemini", label: "Gemini" },
|
||||
{ value: "antigravity", label: "Antigravity" },
|
||||
{ value: "grok", label: "Grok" },
|
||||
]);
|
||||
|
||||
const compositeRouteEndpointOptions = computed(() => [
|
||||
{ value: "any", label: t("admin.groups.compositeRoutes.endpoints.any") },
|
||||
{
|
||||
value: "messages",
|
||||
label: t("admin.groups.compositeRoutes.endpoints.messages"),
|
||||
},
|
||||
{
|
||||
value: "count_tokens",
|
||||
label: t("admin.groups.compositeRoutes.endpoints.countTokens"),
|
||||
},
|
||||
{
|
||||
value: "responses",
|
||||
label: t("admin.groups.compositeRoutes.endpoints.responses"),
|
||||
},
|
||||
{
|
||||
value: "chat_completions",
|
||||
label: t("admin.groups.compositeRoutes.endpoints.chatCompletions"),
|
||||
},
|
||||
{
|
||||
value: "embeddings",
|
||||
label: t("admin.groups.compositeRoutes.endpoints.embeddings"),
|
||||
},
|
||||
{ value: "images", label: t("admin.groups.compositeRoutes.endpoints.images") },
|
||||
{ value: "gemini", label: t("admin.groups.compositeRoutes.endpoints.gemini") },
|
||||
]);
|
||||
|
||||
const compositeRouteMatchOptions = computed(() => [
|
||||
{ value: "exact", label: t("admin.groups.compositeRoutes.match.exact") },
|
||||
{ value: "prefix", label: t("admin.groups.compositeRoutes.match.prefix") },
|
||||
]);
|
||||
|
||||
const editStatusOptions = computed(() => [
|
||||
{ value: "active", label: t("admin.accounts.status.active") },
|
||||
{ value: "inactive", label: t("admin.accounts.status.inactive") },
|
||||
@@ -4030,6 +4444,38 @@ const rateMultipliersGroup = ref<AdminGroup | null>(null);
|
||||
const showRPMOverridesModal = ref(false);
|
||||
const rpmOverridesGroup = ref<AdminGroup | null>(null);
|
||||
const sortableGroups = ref<AdminGroup[]>([]);
|
||||
type ConcreteGroupPlatform = Exclude<GroupPlatform, "composite">;
|
||||
type CompositeRouteFormState = {
|
||||
public_model: string;
|
||||
match_type: CompositeRouteMatchType;
|
||||
target_platform: ConcreteGroupPlatform;
|
||||
upstream_model: string;
|
||||
endpoint: CompositeRouteEndpoint;
|
||||
priority: number;
|
||||
enabled: boolean;
|
||||
notes: string;
|
||||
};
|
||||
|
||||
const showCompositeRoutesModal = ref(false);
|
||||
const compositeRoutesGroup = ref<AdminGroup | null>(null);
|
||||
const compositeRoutes = ref<CompositeModelRoute[]>([]);
|
||||
const compositeRoutesLoading = ref(false);
|
||||
const compositeRouteSaving = ref(false);
|
||||
const compositeRouteEditingId = ref<number | null>(null);
|
||||
const compositePreviewModel = ref("");
|
||||
const compositePreviewEndpoint = ref<CompositeRouteEndpoint>("any");
|
||||
const compositePreviewLoading = ref(false);
|
||||
const compositePreviewDecision = ref<CompositeRouteDecision | null>(null);
|
||||
const compositeRouteForm = reactive<CompositeRouteFormState>({
|
||||
public_model: "",
|
||||
match_type: "exact",
|
||||
target_platform: "openai",
|
||||
upstream_model: "",
|
||||
endpoint: "any",
|
||||
priority: 100,
|
||||
enabled: true,
|
||||
notes: "",
|
||||
});
|
||||
const createMessagesDispatchDefaults = createDefaultMessagesDispatchFormState();
|
||||
const editMessagesDispatchDefaults = createDefaultMessagesDispatchFormState();
|
||||
const createModelsListState = reactive(createInitialModelsListState());
|
||||
@@ -5247,6 +5693,188 @@ const handleDuplicate = async (group: AdminGroup) => {
|
||||
}
|
||||
};
|
||||
|
||||
const compositeRouteMatchLabel = (matchType: CompositeRouteMatchType) =>
|
||||
compositeRouteMatchOptions.value.find((option) => option.value === matchType)
|
||||
?.label || matchType;
|
||||
|
||||
const formatCompositeEndpoint = (endpoint: CompositeRouteEndpoint) =>
|
||||
compositeRouteEndpointOptions.value.find((option) => option.value === endpoint)
|
||||
?.label || endpoint;
|
||||
|
||||
const formatCompositePlatform = (platform: string) => {
|
||||
if (!platform) return "—";
|
||||
return t(`admin.groups.platforms.${platform}`);
|
||||
};
|
||||
|
||||
const compositeRouteSourceLabel = (source: string) => {
|
||||
if (source === "route") return t("admin.groups.compositeRoutes.sources.route");
|
||||
if (source === "detector") {
|
||||
return t("admin.groups.compositeRoutes.sources.detector");
|
||||
}
|
||||
return source || "—";
|
||||
};
|
||||
|
||||
const resetCompositeRouteForm = () => {
|
||||
compositeRouteEditingId.value = null;
|
||||
compositeRouteForm.public_model = "";
|
||||
compositeRouteForm.match_type = "exact";
|
||||
compositeRouteForm.target_platform = "openai";
|
||||
compositeRouteForm.upstream_model = "";
|
||||
compositeRouteForm.endpoint = "any";
|
||||
compositeRouteForm.priority = 100;
|
||||
compositeRouteForm.enabled = true;
|
||||
compositeRouteForm.notes = "";
|
||||
};
|
||||
|
||||
const toCompositeRouteInput = (): CompositeModelRouteInput => ({
|
||||
public_model: compositeRouteForm.public_model.trim(),
|
||||
match_type: compositeRouteForm.match_type,
|
||||
target_platform: compositeRouteForm.target_platform,
|
||||
upstream_model: compositeRouteForm.upstream_model.trim(),
|
||||
endpoint: compositeRouteForm.endpoint,
|
||||
priority: Number(compositeRouteForm.priority) || 100,
|
||||
enabled: compositeRouteForm.enabled,
|
||||
notes: compositeRouteForm.notes.trim(),
|
||||
});
|
||||
|
||||
const loadCompositeRoutes = async () => {
|
||||
if (!compositeRoutesGroup.value) return;
|
||||
compositeRoutesLoading.value = true;
|
||||
try {
|
||||
const routes = await adminAPI.groups.listCompositeRoutes(
|
||||
compositeRoutesGroup.value.id,
|
||||
);
|
||||
compositeRoutes.value = routes.sort((a, b) => {
|
||||
if (a.priority !== b.priority) return a.priority - b.priority;
|
||||
return a.id - b.id;
|
||||
});
|
||||
} catch (error: any) {
|
||||
appStore.showError(
|
||||
error.response?.data?.detail ||
|
||||
error.response?.data?.message ||
|
||||
t("admin.groups.compositeRoutes.failedToLoad"),
|
||||
);
|
||||
console.error("Error loading composite routes:", error);
|
||||
} finally {
|
||||
compositeRoutesLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleCompositeRoutes = async (group: AdminGroup) => {
|
||||
compositeRoutesGroup.value = group;
|
||||
compositePreviewModel.value = "";
|
||||
compositePreviewEndpoint.value = "any";
|
||||
compositePreviewDecision.value = null;
|
||||
resetCompositeRouteForm();
|
||||
showCompositeRoutesModal.value = true;
|
||||
await loadCompositeRoutes();
|
||||
};
|
||||
|
||||
const closeCompositeRoutesModal = () => {
|
||||
showCompositeRoutesModal.value = false;
|
||||
compositeRoutesGroup.value = null;
|
||||
compositeRoutes.value = [];
|
||||
compositePreviewDecision.value = null;
|
||||
resetCompositeRouteForm();
|
||||
};
|
||||
|
||||
const editCompositeRoute = (route: CompositeModelRoute) => {
|
||||
compositeRouteEditingId.value = route.id;
|
||||
compositeRouteForm.public_model = route.public_model;
|
||||
compositeRouteForm.match_type = route.match_type;
|
||||
compositeRouteForm.target_platform = route.target_platform;
|
||||
compositeRouteForm.upstream_model = route.upstream_model;
|
||||
compositeRouteForm.endpoint = route.endpoint;
|
||||
compositeRouteForm.priority = route.priority || 100;
|
||||
compositeRouteForm.enabled = route.enabled;
|
||||
compositeRouteForm.notes = route.notes || "";
|
||||
};
|
||||
|
||||
const saveCompositeRoute = async () => {
|
||||
if (!compositeRoutesGroup.value) return;
|
||||
if (!compositeRouteForm.public_model.trim()) {
|
||||
appStore.showError(t("admin.groups.compositeRoutes.publicModelRequired"));
|
||||
return;
|
||||
}
|
||||
compositeRouteSaving.value = true;
|
||||
try {
|
||||
const payload = toCompositeRouteInput();
|
||||
if (compositeRouteEditingId.value) {
|
||||
await adminAPI.groups.updateCompositeRoute(
|
||||
compositeRoutesGroup.value.id,
|
||||
compositeRouteEditingId.value,
|
||||
payload,
|
||||
);
|
||||
appStore.showSuccess(t("admin.groups.compositeRoutes.routeUpdated"));
|
||||
} else {
|
||||
await adminAPI.groups.createCompositeRoute(
|
||||
compositeRoutesGroup.value.id,
|
||||
payload,
|
||||
);
|
||||
appStore.showSuccess(t("admin.groups.compositeRoutes.routeCreated"));
|
||||
}
|
||||
resetCompositeRouteForm();
|
||||
await loadCompositeRoutes();
|
||||
} catch (error: any) {
|
||||
appStore.showError(
|
||||
error.response?.data?.detail ||
|
||||
error.response?.data?.message ||
|
||||
t("admin.groups.compositeRoutes.failedToSave"),
|
||||
);
|
||||
console.error("Error saving composite route:", error);
|
||||
} finally {
|
||||
compositeRouteSaving.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const deleteCompositeRoute = async (route: CompositeModelRoute) => {
|
||||
if (!compositeRoutesGroup.value) return;
|
||||
if (!window.confirm(t("admin.groups.compositeRoutes.deleteConfirm"))) return;
|
||||
try {
|
||||
await adminAPI.groups.deleteCompositeRoute(
|
||||
compositeRoutesGroup.value.id,
|
||||
route.id,
|
||||
);
|
||||
if (compositeRouteEditingId.value === route.id) {
|
||||
resetCompositeRouteForm();
|
||||
}
|
||||
appStore.showSuccess(t("admin.groups.compositeRoutes.routeDeleted"));
|
||||
await loadCompositeRoutes();
|
||||
} catch (error: any) {
|
||||
appStore.showError(
|
||||
error.response?.data?.detail ||
|
||||
error.response?.data?.message ||
|
||||
t("admin.groups.compositeRoutes.failedToDelete"),
|
||||
);
|
||||
console.error("Error deleting composite route:", error);
|
||||
}
|
||||
};
|
||||
|
||||
const previewCompositeRoute = async () => {
|
||||
if (!compositeRoutesGroup.value || !compositePreviewModel.value.trim()) {
|
||||
return;
|
||||
}
|
||||
compositePreviewLoading.value = true;
|
||||
try {
|
||||
compositePreviewDecision.value = await adminAPI.groups.previewCompositeRoute(
|
||||
compositeRoutesGroup.value.id,
|
||||
{
|
||||
model: compositePreviewModel.value.trim(),
|
||||
endpoint: compositePreviewEndpoint.value,
|
||||
},
|
||||
);
|
||||
} catch (error: any) {
|
||||
appStore.showError(
|
||||
error.response?.data?.detail ||
|
||||
error.response?.data?.message ||
|
||||
t("admin.groups.compositeRoutes.failedToPreview"),
|
||||
);
|
||||
console.error("Error previewing composite route:", error);
|
||||
} finally {
|
||||
compositePreviewLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleDelete = (group: AdminGroup) => {
|
||||
deletingGroup.value = group;
|
||||
showDeleteDialog.value = true;
|
||||
|
||||
Reference in New Issue
Block a user