From a008b63c1644d66f2a9eff27598a1d8bf9370c76 Mon Sep 17 00:00:00 2001 From: Heatherm Huang Date: Wed, 1 Jul 2026 09:27:47 +0800 Subject: [PATCH] Add composite group route registry --- backend/cmd/server/wire_gen.go | 8 +- backend/ent/client.go | 189 ++- backend/ent/compositemodelroute.go | 264 ++++ .../compositemodelroute.go | 200 +++ backend/ent/compositemodelroute/where.go | 754 ++++++++++ backend/ent/compositemodelroute_create.go | 1238 +++++++++++++++++ backend/ent/compositemodelroute_delete.go | 88 ++ backend/ent/compositemodelroute_query.go | 643 +++++++++ backend/ent/compositemodelroute_update.go | 767 ++++++++++ backend/ent/ent.go | 2 + backend/ent/hook/hook.go | 12 + backend/ent/intercept/intercept.go | 30 + backend/ent/migrate/schema.go | 67 + backend/ent/mutation.go | 1053 ++++++++++++++ backend/ent/predicate/predicate.go | 3 + backend/ent/runtime/runtime.go | 70 + backend/ent/schema/composite_model_route.go | 87 ++ backend/ent/tx.go | 3 + .../admin/admin_basic_handlers_test.go | 54 +- .../handler/admin/admin_service_stub_test.go | 58 + .../internal/handler/admin/group_handler.go | 143 ++ ...eway_handler_warmup_intercept_unit_test.go | 1 + .../internal/handler/gateway_models_test.go | 2 +- .../internal/handler/gemini_v1beta_handler.go | 6 + backend/internal/handler/ops_error_logger.go | 2 +- backend/internal/pkg/ctxkey/ctxkey.go | 9 + .../repository/composite_model_route_repo.go | 113 ++ backend/internal/repository/group_repo.go | 7 +- backend/internal/repository/wire.go | 1 + backend/internal/server/api_contract_test.go | 2 +- backend/internal/server/http.go | 3 +- backend/internal/server/router.go | 6 +- backend/internal/server/routes/admin.go | 5 + .../server/routes/composite_platform_test.go | 135 +- backend/internal/server/routes/gateway.go | 97 +- .../internal/server/routes/gateway_test.go | 20 +- backend/internal/service/admin_group.go | 128 ++ backend/internal/service/admin_service.go | 11 + .../service/admin_service_group_test.go | 196 +++ .../internal/service/composite_model_route.go | 126 ++ .../internal/service/composite_platform.go | 83 ++ .../service/composite_platform_test.go | 13 +- .../service/composite_route_resolver.go | 140 ++ .../service/composite_route_resolver_test.go | 135 ++ .../service/gateway_record_usage_test.go | 1 + .../internal/service/gateway_scheduling.go | 24 +- backend/internal/service/gateway_service.go | 3 + backend/internal/service/wire.go | 1 + .../migrations/172_composite_model_routes.sql | 30 + docs/COMPOSITE_GROUPS.md | 48 +- frontend/src/api/admin/groups.ts | 58 + .../src/i18n/locales/en/admin/overview.ts | 49 + .../src/i18n/locales/zh/admin/overview.ts | 49 + frontend/src/types/index.ts | 57 + frontend/src/views/admin/GroupsView.vue | 630 ++++++++- 55 files changed, 7867 insertions(+), 57 deletions(-) create mode 100644 backend/ent/compositemodelroute.go create mode 100644 backend/ent/compositemodelroute/compositemodelroute.go create mode 100644 backend/ent/compositemodelroute/where.go create mode 100644 backend/ent/compositemodelroute_create.go create mode 100644 backend/ent/compositemodelroute_delete.go create mode 100644 backend/ent/compositemodelroute_query.go create mode 100644 backend/ent/compositemodelroute_update.go create mode 100644 backend/ent/schema/composite_model_route.go create mode 100644 backend/internal/repository/composite_model_route_repo.go create mode 100644 backend/internal/service/composite_model_route.go create mode 100644 backend/internal/service/composite_route_resolver.go create mode 100644 backend/internal/service/composite_route_resolver_test.go create mode 100644 backend/migrations/172_composite_model_routes.sql diff --git a/backend/cmd/server/wire_gen.go b/backend/cmd/server/wire_gen.go index 96eb7c417b..f9d7b6fe9e 100644 --- a/backend/cmd/server/wire_gen.go +++ b/backend/cmd/server/wire_gen.go @@ -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) diff --git a/backend/ent/client.go b/backend/ent/client.go index e9b74fcfef..174d0fb2fd 100644 --- a/backend/ent/client.go +++ b/backend/ent/client.go @@ -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 } ) diff --git a/backend/ent/compositemodelroute.go b/backend/ent/compositemodelroute.go new file mode 100644 index 0000000000..4c6637d544 --- /dev/null +++ b/backend/ent/compositemodelroute.go @@ -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 diff --git a/backend/ent/compositemodelroute/compositemodelroute.go b/backend/ent/compositemodelroute/compositemodelroute.go new file mode 100644 index 0000000000..b0709d5488 --- /dev/null +++ b/backend/ent/compositemodelroute/compositemodelroute.go @@ -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), + ) +} diff --git a/backend/ent/compositemodelroute/where.go b/backend/ent/compositemodelroute/where.go new file mode 100644 index 0000000000..61f881f657 --- /dev/null +++ b/backend/ent/compositemodelroute/where.go @@ -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)) +} diff --git a/backend/ent/compositemodelroute_create.go b/backend/ent/compositemodelroute_create.go new file mode 100644 index 0000000000..0dd20edf4d --- /dev/null +++ b/backend/ent/compositemodelroute_create.go @@ -0,0 +1,1238 @@ +// 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" +) + +// CompositeModelRouteCreate is the builder for creating a CompositeModelRoute entity. +type CompositeModelRouteCreate struct { + config + mutation *CompositeModelRouteMutation + hooks []Hook + conflict []sql.ConflictOption +} + +// SetCreatedAt sets the "created_at" field. +func (_c *CompositeModelRouteCreate) SetCreatedAt(v time.Time) *CompositeModelRouteCreate { + _c.mutation.SetCreatedAt(v) + return _c +} + +// SetNillableCreatedAt sets the "created_at" field if the given value is not nil. +func (_c *CompositeModelRouteCreate) SetNillableCreatedAt(v *time.Time) *CompositeModelRouteCreate { + if v != nil { + _c.SetCreatedAt(*v) + } + return _c +} + +// SetUpdatedAt sets the "updated_at" field. +func (_c *CompositeModelRouteCreate) SetUpdatedAt(v time.Time) *CompositeModelRouteCreate { + _c.mutation.SetUpdatedAt(v) + return _c +} + +// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil. +func (_c *CompositeModelRouteCreate) SetNillableUpdatedAt(v *time.Time) *CompositeModelRouteCreate { + if v != nil { + _c.SetUpdatedAt(*v) + } + return _c +} + +// SetDeletedAt sets the "deleted_at" field. +func (_c *CompositeModelRouteCreate) SetDeletedAt(v time.Time) *CompositeModelRouteCreate { + _c.mutation.SetDeletedAt(v) + return _c +} + +// SetNillableDeletedAt sets the "deleted_at" field if the given value is not nil. +func (_c *CompositeModelRouteCreate) SetNillableDeletedAt(v *time.Time) *CompositeModelRouteCreate { + if v != nil { + _c.SetDeletedAt(*v) + } + return _c +} + +// SetGroupID sets the "group_id" field. +func (_c *CompositeModelRouteCreate) SetGroupID(v int64) *CompositeModelRouteCreate { + _c.mutation.SetGroupID(v) + return _c +} + +// SetPublicModel sets the "public_model" field. +func (_c *CompositeModelRouteCreate) SetPublicModel(v string) *CompositeModelRouteCreate { + _c.mutation.SetPublicModel(v) + return _c +} + +// SetMatchType sets the "match_type" field. +func (_c *CompositeModelRouteCreate) SetMatchType(v string) *CompositeModelRouteCreate { + _c.mutation.SetMatchType(v) + return _c +} + +// SetNillableMatchType sets the "match_type" field if the given value is not nil. +func (_c *CompositeModelRouteCreate) SetNillableMatchType(v *string) *CompositeModelRouteCreate { + if v != nil { + _c.SetMatchType(*v) + } + return _c +} + +// SetTargetPlatform sets the "target_platform" field. +func (_c *CompositeModelRouteCreate) SetTargetPlatform(v string) *CompositeModelRouteCreate { + _c.mutation.SetTargetPlatform(v) + return _c +} + +// SetNillableTargetPlatform sets the "target_platform" field if the given value is not nil. +func (_c *CompositeModelRouteCreate) SetNillableTargetPlatform(v *string) *CompositeModelRouteCreate { + if v != nil { + _c.SetTargetPlatform(*v) + } + return _c +} + +// SetUpstreamModel sets the "upstream_model" field. +func (_c *CompositeModelRouteCreate) SetUpstreamModel(v string) *CompositeModelRouteCreate { + _c.mutation.SetUpstreamModel(v) + return _c +} + +// SetNillableUpstreamModel sets the "upstream_model" field if the given value is not nil. +func (_c *CompositeModelRouteCreate) SetNillableUpstreamModel(v *string) *CompositeModelRouteCreate { + if v != nil { + _c.SetUpstreamModel(*v) + } + return _c +} + +// SetEndpoint sets the "endpoint" field. +func (_c *CompositeModelRouteCreate) SetEndpoint(v string) *CompositeModelRouteCreate { + _c.mutation.SetEndpoint(v) + return _c +} + +// SetNillableEndpoint sets the "endpoint" field if the given value is not nil. +func (_c *CompositeModelRouteCreate) SetNillableEndpoint(v *string) *CompositeModelRouteCreate { + if v != nil { + _c.SetEndpoint(*v) + } + return _c +} + +// SetPriority sets the "priority" field. +func (_c *CompositeModelRouteCreate) SetPriority(v int) *CompositeModelRouteCreate { + _c.mutation.SetPriority(v) + return _c +} + +// SetNillablePriority sets the "priority" field if the given value is not nil. +func (_c *CompositeModelRouteCreate) SetNillablePriority(v *int) *CompositeModelRouteCreate { + if v != nil { + _c.SetPriority(*v) + } + return _c +} + +// SetEnabled sets the "enabled" field. +func (_c *CompositeModelRouteCreate) SetEnabled(v bool) *CompositeModelRouteCreate { + _c.mutation.SetEnabled(v) + return _c +} + +// SetNillableEnabled sets the "enabled" field if the given value is not nil. +func (_c *CompositeModelRouteCreate) SetNillableEnabled(v *bool) *CompositeModelRouteCreate { + if v != nil { + _c.SetEnabled(*v) + } + return _c +} + +// SetNotes sets the "notes" field. +func (_c *CompositeModelRouteCreate) SetNotes(v string) *CompositeModelRouteCreate { + _c.mutation.SetNotes(v) + return _c +} + +// SetNillableNotes sets the "notes" field if the given value is not nil. +func (_c *CompositeModelRouteCreate) SetNillableNotes(v *string) *CompositeModelRouteCreate { + if v != nil { + _c.SetNotes(*v) + } + return _c +} + +// SetGroup sets the "group" edge to the Group entity. +func (_c *CompositeModelRouteCreate) SetGroup(v *Group) *CompositeModelRouteCreate { + return _c.SetGroupID(v.ID) +} + +// Mutation returns the CompositeModelRouteMutation object of the builder. +func (_c *CompositeModelRouteCreate) Mutation() *CompositeModelRouteMutation { + return _c.mutation +} + +// Save creates the CompositeModelRoute in the database. +func (_c *CompositeModelRouteCreate) Save(ctx context.Context) (*CompositeModelRoute, error) { + if err := _c.defaults(); err != nil { + return nil, err + } + return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (_c *CompositeModelRouteCreate) SaveX(ctx context.Context) *CompositeModelRoute { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *CompositeModelRouteCreate) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *CompositeModelRouteCreate) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_c *CompositeModelRouteCreate) defaults() error { + if _, ok := _c.mutation.CreatedAt(); !ok { + if compositemodelroute.DefaultCreatedAt == nil { + return fmt.Errorf("ent: uninitialized compositemodelroute.DefaultCreatedAt (forgotten import ent/runtime?)") + } + v := compositemodelroute.DefaultCreatedAt() + _c.mutation.SetCreatedAt(v) + } + if _, ok := _c.mutation.UpdatedAt(); !ok { + if compositemodelroute.DefaultUpdatedAt == nil { + return fmt.Errorf("ent: uninitialized compositemodelroute.DefaultUpdatedAt (forgotten import ent/runtime?)") + } + v := compositemodelroute.DefaultUpdatedAt() + _c.mutation.SetUpdatedAt(v) + } + if _, ok := _c.mutation.MatchType(); !ok { + v := compositemodelroute.DefaultMatchType + _c.mutation.SetMatchType(v) + } + if _, ok := _c.mutation.TargetPlatform(); !ok { + v := compositemodelroute.DefaultTargetPlatform + _c.mutation.SetTargetPlatform(v) + } + if _, ok := _c.mutation.UpstreamModel(); !ok { + v := compositemodelroute.DefaultUpstreamModel + _c.mutation.SetUpstreamModel(v) + } + if _, ok := _c.mutation.Endpoint(); !ok { + v := compositemodelroute.DefaultEndpoint + _c.mutation.SetEndpoint(v) + } + if _, ok := _c.mutation.Priority(); !ok { + v := compositemodelroute.DefaultPriority + _c.mutation.SetPriority(v) + } + if _, ok := _c.mutation.Enabled(); !ok { + v := compositemodelroute.DefaultEnabled + _c.mutation.SetEnabled(v) + } + return nil +} + +// check runs all checks and user-defined validators on the builder. +func (_c *CompositeModelRouteCreate) check() error { + if _, ok := _c.mutation.CreatedAt(); !ok { + return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "CompositeModelRoute.created_at"`)} + } + if _, ok := _c.mutation.UpdatedAt(); !ok { + return &ValidationError{Name: "updated_at", err: errors.New(`ent: missing required field "CompositeModelRoute.updated_at"`)} + } + if _, ok := _c.mutation.GroupID(); !ok { + return &ValidationError{Name: "group_id", err: errors.New(`ent: missing required field "CompositeModelRoute.group_id"`)} + } + if _, ok := _c.mutation.PublicModel(); !ok { + return &ValidationError{Name: "public_model", err: errors.New(`ent: missing required field "CompositeModelRoute.public_model"`)} + } + if v, ok := _c.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 _, ok := _c.mutation.MatchType(); !ok { + return &ValidationError{Name: "match_type", err: errors.New(`ent: missing required field "CompositeModelRoute.match_type"`)} + } + if v, ok := _c.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 _, ok := _c.mutation.TargetPlatform(); !ok { + return &ValidationError{Name: "target_platform", err: errors.New(`ent: missing required field "CompositeModelRoute.target_platform"`)} + } + if v, ok := _c.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 _, ok := _c.mutation.UpstreamModel(); !ok { + return &ValidationError{Name: "upstream_model", err: errors.New(`ent: missing required field "CompositeModelRoute.upstream_model"`)} + } + if v, ok := _c.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 _, ok := _c.mutation.Endpoint(); !ok { + return &ValidationError{Name: "endpoint", err: errors.New(`ent: missing required field "CompositeModelRoute.endpoint"`)} + } + if v, ok := _c.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 _, ok := _c.mutation.Priority(); !ok { + return &ValidationError{Name: "priority", err: errors.New(`ent: missing required field "CompositeModelRoute.priority"`)} + } + if _, ok := _c.mutation.Enabled(); !ok { + return &ValidationError{Name: "enabled", err: errors.New(`ent: missing required field "CompositeModelRoute.enabled"`)} + } + if len(_c.mutation.GroupIDs()) == 0 { + return &ValidationError{Name: "group", err: errors.New(`ent: missing required edge "CompositeModelRoute.group"`)} + } + return nil +} + +func (_c *CompositeModelRouteCreate) sqlSave(ctx context.Context) (*CompositeModelRoute, error) { + if err := _c.check(); err != nil { + return nil, err + } + _node, _spec := _c.createSpec() + if err := sqlgraph.CreateNode(ctx, _c.driver, _spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + id := _spec.ID.Value.(int64) + _node.ID = int64(id) + _c.mutation.id = &_node.ID + _c.mutation.done = true + return _node, nil +} + +func (_c *CompositeModelRouteCreate) createSpec() (*CompositeModelRoute, *sqlgraph.CreateSpec) { + var ( + _node = &CompositeModelRoute{config: _c.config} + _spec = sqlgraph.NewCreateSpec(compositemodelroute.Table, sqlgraph.NewFieldSpec(compositemodelroute.FieldID, field.TypeInt64)) + ) + _spec.OnConflict = _c.conflict + if value, ok := _c.mutation.CreatedAt(); ok { + _spec.SetField(compositemodelroute.FieldCreatedAt, field.TypeTime, value) + _node.CreatedAt = value + } + if value, ok := _c.mutation.UpdatedAt(); ok { + _spec.SetField(compositemodelroute.FieldUpdatedAt, field.TypeTime, value) + _node.UpdatedAt = value + } + if value, ok := _c.mutation.DeletedAt(); ok { + _spec.SetField(compositemodelroute.FieldDeletedAt, field.TypeTime, value) + _node.DeletedAt = &value + } + if value, ok := _c.mutation.PublicModel(); ok { + _spec.SetField(compositemodelroute.FieldPublicModel, field.TypeString, value) + _node.PublicModel = value + } + if value, ok := _c.mutation.MatchType(); ok { + _spec.SetField(compositemodelroute.FieldMatchType, field.TypeString, value) + _node.MatchType = value + } + if value, ok := _c.mutation.TargetPlatform(); ok { + _spec.SetField(compositemodelroute.FieldTargetPlatform, field.TypeString, value) + _node.TargetPlatform = value + } + if value, ok := _c.mutation.UpstreamModel(); ok { + _spec.SetField(compositemodelroute.FieldUpstreamModel, field.TypeString, value) + _node.UpstreamModel = value + } + if value, ok := _c.mutation.Endpoint(); ok { + _spec.SetField(compositemodelroute.FieldEndpoint, field.TypeString, value) + _node.Endpoint = value + } + if value, ok := _c.mutation.Priority(); ok { + _spec.SetField(compositemodelroute.FieldPriority, field.TypeInt, value) + _node.Priority = value + } + if value, ok := _c.mutation.Enabled(); ok { + _spec.SetField(compositemodelroute.FieldEnabled, field.TypeBool, value) + _node.Enabled = value + } + if value, ok := _c.mutation.Notes(); ok { + _spec.SetField(compositemodelroute.FieldNotes, field.TypeString, value) + _node.Notes = &value + } + if nodes := _c.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) + } + _node.GroupID = nodes[0] + _spec.Edges = append(_spec.Edges, edge) + } + return _node, _spec +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.CompositeModelRoute.Create(). +// SetCreatedAt(v). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.CompositeModelRouteUpsert) { +// SetCreatedAt(v+v). +// }). +// Exec(ctx) +func (_c *CompositeModelRouteCreate) OnConflict(opts ...sql.ConflictOption) *CompositeModelRouteUpsertOne { + _c.conflict = opts + return &CompositeModelRouteUpsertOne{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.CompositeModelRoute.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *CompositeModelRouteCreate) OnConflictColumns(columns ...string) *CompositeModelRouteUpsertOne { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &CompositeModelRouteUpsertOne{ + create: _c, + } +} + +type ( + // CompositeModelRouteUpsertOne is the builder for "upsert"-ing + // one CompositeModelRoute node. + CompositeModelRouteUpsertOne struct { + create *CompositeModelRouteCreate + } + + // CompositeModelRouteUpsert is the "OnConflict" setter. + CompositeModelRouteUpsert struct { + *sql.UpdateSet + } +) + +// SetUpdatedAt sets the "updated_at" field. +func (u *CompositeModelRouteUpsert) SetUpdatedAt(v time.Time) *CompositeModelRouteUpsert { + u.Set(compositemodelroute.FieldUpdatedAt, v) + return u +} + +// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create. +func (u *CompositeModelRouteUpsert) UpdateUpdatedAt() *CompositeModelRouteUpsert { + u.SetExcluded(compositemodelroute.FieldUpdatedAt) + return u +} + +// SetDeletedAt sets the "deleted_at" field. +func (u *CompositeModelRouteUpsert) SetDeletedAt(v time.Time) *CompositeModelRouteUpsert { + u.Set(compositemodelroute.FieldDeletedAt, v) + return u +} + +// UpdateDeletedAt sets the "deleted_at" field to the value that was provided on create. +func (u *CompositeModelRouteUpsert) UpdateDeletedAt() *CompositeModelRouteUpsert { + u.SetExcluded(compositemodelroute.FieldDeletedAt) + return u +} + +// ClearDeletedAt clears the value of the "deleted_at" field. +func (u *CompositeModelRouteUpsert) ClearDeletedAt() *CompositeModelRouteUpsert { + u.SetNull(compositemodelroute.FieldDeletedAt) + return u +} + +// SetGroupID sets the "group_id" field. +func (u *CompositeModelRouteUpsert) SetGroupID(v int64) *CompositeModelRouteUpsert { + u.Set(compositemodelroute.FieldGroupID, v) + return u +} + +// UpdateGroupID sets the "group_id" field to the value that was provided on create. +func (u *CompositeModelRouteUpsert) UpdateGroupID() *CompositeModelRouteUpsert { + u.SetExcluded(compositemodelroute.FieldGroupID) + return u +} + +// SetPublicModel sets the "public_model" field. +func (u *CompositeModelRouteUpsert) SetPublicModel(v string) *CompositeModelRouteUpsert { + u.Set(compositemodelroute.FieldPublicModel, v) + return u +} + +// UpdatePublicModel sets the "public_model" field to the value that was provided on create. +func (u *CompositeModelRouteUpsert) UpdatePublicModel() *CompositeModelRouteUpsert { + u.SetExcluded(compositemodelroute.FieldPublicModel) + return u +} + +// SetMatchType sets the "match_type" field. +func (u *CompositeModelRouteUpsert) SetMatchType(v string) *CompositeModelRouteUpsert { + u.Set(compositemodelroute.FieldMatchType, v) + return u +} + +// UpdateMatchType sets the "match_type" field to the value that was provided on create. +func (u *CompositeModelRouteUpsert) UpdateMatchType() *CompositeModelRouteUpsert { + u.SetExcluded(compositemodelroute.FieldMatchType) + return u +} + +// SetTargetPlatform sets the "target_platform" field. +func (u *CompositeModelRouteUpsert) SetTargetPlatform(v string) *CompositeModelRouteUpsert { + u.Set(compositemodelroute.FieldTargetPlatform, v) + return u +} + +// UpdateTargetPlatform sets the "target_platform" field to the value that was provided on create. +func (u *CompositeModelRouteUpsert) UpdateTargetPlatform() *CompositeModelRouteUpsert { + u.SetExcluded(compositemodelroute.FieldTargetPlatform) + return u +} + +// SetUpstreamModel sets the "upstream_model" field. +func (u *CompositeModelRouteUpsert) SetUpstreamModel(v string) *CompositeModelRouteUpsert { + u.Set(compositemodelroute.FieldUpstreamModel, v) + return u +} + +// UpdateUpstreamModel sets the "upstream_model" field to the value that was provided on create. +func (u *CompositeModelRouteUpsert) UpdateUpstreamModel() *CompositeModelRouteUpsert { + u.SetExcluded(compositemodelroute.FieldUpstreamModel) + return u +} + +// SetEndpoint sets the "endpoint" field. +func (u *CompositeModelRouteUpsert) SetEndpoint(v string) *CompositeModelRouteUpsert { + u.Set(compositemodelroute.FieldEndpoint, v) + return u +} + +// UpdateEndpoint sets the "endpoint" field to the value that was provided on create. +func (u *CompositeModelRouteUpsert) UpdateEndpoint() *CompositeModelRouteUpsert { + u.SetExcluded(compositemodelroute.FieldEndpoint) + return u +} + +// SetPriority sets the "priority" field. +func (u *CompositeModelRouteUpsert) SetPriority(v int) *CompositeModelRouteUpsert { + u.Set(compositemodelroute.FieldPriority, v) + return u +} + +// UpdatePriority sets the "priority" field to the value that was provided on create. +func (u *CompositeModelRouteUpsert) UpdatePriority() *CompositeModelRouteUpsert { + u.SetExcluded(compositemodelroute.FieldPriority) + return u +} + +// AddPriority adds v to the "priority" field. +func (u *CompositeModelRouteUpsert) AddPriority(v int) *CompositeModelRouteUpsert { + u.Add(compositemodelroute.FieldPriority, v) + return u +} + +// SetEnabled sets the "enabled" field. +func (u *CompositeModelRouteUpsert) SetEnabled(v bool) *CompositeModelRouteUpsert { + u.Set(compositemodelroute.FieldEnabled, v) + return u +} + +// UpdateEnabled sets the "enabled" field to the value that was provided on create. +func (u *CompositeModelRouteUpsert) UpdateEnabled() *CompositeModelRouteUpsert { + u.SetExcluded(compositemodelroute.FieldEnabled) + return u +} + +// SetNotes sets the "notes" field. +func (u *CompositeModelRouteUpsert) SetNotes(v string) *CompositeModelRouteUpsert { + u.Set(compositemodelroute.FieldNotes, v) + return u +} + +// UpdateNotes sets the "notes" field to the value that was provided on create. +func (u *CompositeModelRouteUpsert) UpdateNotes() *CompositeModelRouteUpsert { + u.SetExcluded(compositemodelroute.FieldNotes) + return u +} + +// ClearNotes clears the value of the "notes" field. +func (u *CompositeModelRouteUpsert) ClearNotes() *CompositeModelRouteUpsert { + u.SetNull(compositemodelroute.FieldNotes) + return u +} + +// UpdateNewValues updates the mutable fields using the new values that were set on create. +// Using this option is equivalent to using: +// +// client.CompositeModelRoute.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// ). +// Exec(ctx) +func (u *CompositeModelRouteUpsertOne) UpdateNewValues() *CompositeModelRouteUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + if _, exists := u.create.mutation.CreatedAt(); exists { + s.SetIgnore(compositemodelroute.FieldCreatedAt) + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.CompositeModelRoute.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *CompositeModelRouteUpsertOne) Ignore() *CompositeModelRouteUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *CompositeModelRouteUpsertOne) DoNothing() *CompositeModelRouteUpsertOne { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the CompositeModelRouteCreate.OnConflict +// documentation for more info. +func (u *CompositeModelRouteUpsertOne) Update(set func(*CompositeModelRouteUpsert)) *CompositeModelRouteUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&CompositeModelRouteUpsert{UpdateSet: update}) + })) + return u +} + +// SetUpdatedAt sets the "updated_at" field. +func (u *CompositeModelRouteUpsertOne) SetUpdatedAt(v time.Time) *CompositeModelRouteUpsertOne { + return u.Update(func(s *CompositeModelRouteUpsert) { + s.SetUpdatedAt(v) + }) +} + +// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create. +func (u *CompositeModelRouteUpsertOne) UpdateUpdatedAt() *CompositeModelRouteUpsertOne { + return u.Update(func(s *CompositeModelRouteUpsert) { + s.UpdateUpdatedAt() + }) +} + +// SetDeletedAt sets the "deleted_at" field. +func (u *CompositeModelRouteUpsertOne) SetDeletedAt(v time.Time) *CompositeModelRouteUpsertOne { + return u.Update(func(s *CompositeModelRouteUpsert) { + s.SetDeletedAt(v) + }) +} + +// UpdateDeletedAt sets the "deleted_at" field to the value that was provided on create. +func (u *CompositeModelRouteUpsertOne) UpdateDeletedAt() *CompositeModelRouteUpsertOne { + return u.Update(func(s *CompositeModelRouteUpsert) { + s.UpdateDeletedAt() + }) +} + +// ClearDeletedAt clears the value of the "deleted_at" field. +func (u *CompositeModelRouteUpsertOne) ClearDeletedAt() *CompositeModelRouteUpsertOne { + return u.Update(func(s *CompositeModelRouteUpsert) { + s.ClearDeletedAt() + }) +} + +// SetGroupID sets the "group_id" field. +func (u *CompositeModelRouteUpsertOne) SetGroupID(v int64) *CompositeModelRouteUpsertOne { + return u.Update(func(s *CompositeModelRouteUpsert) { + s.SetGroupID(v) + }) +} + +// UpdateGroupID sets the "group_id" field to the value that was provided on create. +func (u *CompositeModelRouteUpsertOne) UpdateGroupID() *CompositeModelRouteUpsertOne { + return u.Update(func(s *CompositeModelRouteUpsert) { + s.UpdateGroupID() + }) +} + +// SetPublicModel sets the "public_model" field. +func (u *CompositeModelRouteUpsertOne) SetPublicModel(v string) *CompositeModelRouteUpsertOne { + return u.Update(func(s *CompositeModelRouteUpsert) { + s.SetPublicModel(v) + }) +} + +// UpdatePublicModel sets the "public_model" field to the value that was provided on create. +func (u *CompositeModelRouteUpsertOne) UpdatePublicModel() *CompositeModelRouteUpsertOne { + return u.Update(func(s *CompositeModelRouteUpsert) { + s.UpdatePublicModel() + }) +} + +// SetMatchType sets the "match_type" field. +func (u *CompositeModelRouteUpsertOne) SetMatchType(v string) *CompositeModelRouteUpsertOne { + return u.Update(func(s *CompositeModelRouteUpsert) { + s.SetMatchType(v) + }) +} + +// UpdateMatchType sets the "match_type" field to the value that was provided on create. +func (u *CompositeModelRouteUpsertOne) UpdateMatchType() *CompositeModelRouteUpsertOne { + return u.Update(func(s *CompositeModelRouteUpsert) { + s.UpdateMatchType() + }) +} + +// SetTargetPlatform sets the "target_platform" field. +func (u *CompositeModelRouteUpsertOne) SetTargetPlatform(v string) *CompositeModelRouteUpsertOne { + return u.Update(func(s *CompositeModelRouteUpsert) { + s.SetTargetPlatform(v) + }) +} + +// UpdateTargetPlatform sets the "target_platform" field to the value that was provided on create. +func (u *CompositeModelRouteUpsertOne) UpdateTargetPlatform() *CompositeModelRouteUpsertOne { + return u.Update(func(s *CompositeModelRouteUpsert) { + s.UpdateTargetPlatform() + }) +} + +// SetUpstreamModel sets the "upstream_model" field. +func (u *CompositeModelRouteUpsertOne) SetUpstreamModel(v string) *CompositeModelRouteUpsertOne { + return u.Update(func(s *CompositeModelRouteUpsert) { + s.SetUpstreamModel(v) + }) +} + +// UpdateUpstreamModel sets the "upstream_model" field to the value that was provided on create. +func (u *CompositeModelRouteUpsertOne) UpdateUpstreamModel() *CompositeModelRouteUpsertOne { + return u.Update(func(s *CompositeModelRouteUpsert) { + s.UpdateUpstreamModel() + }) +} + +// SetEndpoint sets the "endpoint" field. +func (u *CompositeModelRouteUpsertOne) SetEndpoint(v string) *CompositeModelRouteUpsertOne { + return u.Update(func(s *CompositeModelRouteUpsert) { + s.SetEndpoint(v) + }) +} + +// UpdateEndpoint sets the "endpoint" field to the value that was provided on create. +func (u *CompositeModelRouteUpsertOne) UpdateEndpoint() *CompositeModelRouteUpsertOne { + return u.Update(func(s *CompositeModelRouteUpsert) { + s.UpdateEndpoint() + }) +} + +// SetPriority sets the "priority" field. +func (u *CompositeModelRouteUpsertOne) SetPriority(v int) *CompositeModelRouteUpsertOne { + return u.Update(func(s *CompositeModelRouteUpsert) { + s.SetPriority(v) + }) +} + +// AddPriority adds v to the "priority" field. +func (u *CompositeModelRouteUpsertOne) AddPriority(v int) *CompositeModelRouteUpsertOne { + return u.Update(func(s *CompositeModelRouteUpsert) { + s.AddPriority(v) + }) +} + +// UpdatePriority sets the "priority" field to the value that was provided on create. +func (u *CompositeModelRouteUpsertOne) UpdatePriority() *CompositeModelRouteUpsertOne { + return u.Update(func(s *CompositeModelRouteUpsert) { + s.UpdatePriority() + }) +} + +// SetEnabled sets the "enabled" field. +func (u *CompositeModelRouteUpsertOne) SetEnabled(v bool) *CompositeModelRouteUpsertOne { + return u.Update(func(s *CompositeModelRouteUpsert) { + s.SetEnabled(v) + }) +} + +// UpdateEnabled sets the "enabled" field to the value that was provided on create. +func (u *CompositeModelRouteUpsertOne) UpdateEnabled() *CompositeModelRouteUpsertOne { + return u.Update(func(s *CompositeModelRouteUpsert) { + s.UpdateEnabled() + }) +} + +// SetNotes sets the "notes" field. +func (u *CompositeModelRouteUpsertOne) SetNotes(v string) *CompositeModelRouteUpsertOne { + return u.Update(func(s *CompositeModelRouteUpsert) { + s.SetNotes(v) + }) +} + +// UpdateNotes sets the "notes" field to the value that was provided on create. +func (u *CompositeModelRouteUpsertOne) UpdateNotes() *CompositeModelRouteUpsertOne { + return u.Update(func(s *CompositeModelRouteUpsert) { + s.UpdateNotes() + }) +} + +// ClearNotes clears the value of the "notes" field. +func (u *CompositeModelRouteUpsertOne) ClearNotes() *CompositeModelRouteUpsertOne { + return u.Update(func(s *CompositeModelRouteUpsert) { + s.ClearNotes() + }) +} + +// Exec executes the query. +func (u *CompositeModelRouteUpsertOne) Exec(ctx context.Context) error { + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for CompositeModelRouteCreate.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *CompositeModelRouteUpsertOne) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} + +// Exec executes the UPSERT query and returns the inserted/updated ID. +func (u *CompositeModelRouteUpsertOne) ID(ctx context.Context) (id int64, err error) { + node, err := u.create.Save(ctx) + if err != nil { + return id, err + } + return node.ID, nil +} + +// IDX is like ID, but panics if an error occurs. +func (u *CompositeModelRouteUpsertOne) IDX(ctx context.Context) int64 { + id, err := u.ID(ctx) + if err != nil { + panic(err) + } + return id +} + +// CompositeModelRouteCreateBulk is the builder for creating many CompositeModelRoute entities in bulk. +type CompositeModelRouteCreateBulk struct { + config + err error + builders []*CompositeModelRouteCreate + conflict []sql.ConflictOption +} + +// Save creates the CompositeModelRoute entities in the database. +func (_c *CompositeModelRouteCreateBulk) Save(ctx context.Context) ([]*CompositeModelRoute, error) { + if _c.err != nil { + return nil, _c.err + } + specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) + nodes := make([]*CompositeModelRoute, len(_c.builders)) + mutators := make([]Mutator, len(_c.builders)) + for i := range _c.builders { + func(i int, root context.Context) { + builder := _c.builders[i] + builder.defaults() + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*CompositeModelRouteMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + if err := builder.check(); err != nil { + return nil, err + } + builder.mutation = mutation + var err error + nodes[i], specs[i] = builder.createSpec() + if i < len(mutators)-1 { + _, err = mutators[i+1].Mutate(root, _c.builders[i+1].mutation) + } else { + spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + spec.OnConflict = _c.conflict + // Invoke the actual operation on the latest mutation in the chain. + if err = sqlgraph.BatchCreate(ctx, _c.driver, spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + } + } + if err != nil { + return nil, err + } + mutation.id = &nodes[i].ID + if specs[i].ID.Value != nil { + id := specs[i].ID.Value.(int64) + nodes[i].ID = int64(id) + } + mutation.done = true + return nodes[i], nil + }) + for i := len(builder.hooks) - 1; i >= 0; i-- { + mut = builder.hooks[i](mut) + } + mutators[i] = mut + }(i, ctx) + } + if len(mutators) > 0 { + if _, err := mutators[0].Mutate(ctx, _c.builders[0].mutation); err != nil { + return nil, err + } + } + return nodes, nil +} + +// SaveX is like Save, but panics if an error occurs. +func (_c *CompositeModelRouteCreateBulk) SaveX(ctx context.Context) []*CompositeModelRoute { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *CompositeModelRouteCreateBulk) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *CompositeModelRouteCreateBulk) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.CompositeModelRoute.CreateBulk(builders...). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.CompositeModelRouteUpsert) { +// SetCreatedAt(v+v). +// }). +// Exec(ctx) +func (_c *CompositeModelRouteCreateBulk) OnConflict(opts ...sql.ConflictOption) *CompositeModelRouteUpsertBulk { + _c.conflict = opts + return &CompositeModelRouteUpsertBulk{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.CompositeModelRoute.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *CompositeModelRouteCreateBulk) OnConflictColumns(columns ...string) *CompositeModelRouteUpsertBulk { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &CompositeModelRouteUpsertBulk{ + create: _c, + } +} + +// CompositeModelRouteUpsertBulk is the builder for "upsert"-ing +// a bulk of CompositeModelRoute nodes. +type CompositeModelRouteUpsertBulk struct { + create *CompositeModelRouteCreateBulk +} + +// UpdateNewValues updates the mutable fields using the new values that +// were set on create. Using this option is equivalent to using: +// +// client.CompositeModelRoute.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// ). +// Exec(ctx) +func (u *CompositeModelRouteUpsertBulk) UpdateNewValues() *CompositeModelRouteUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + for _, b := range u.create.builders { + if _, exists := b.mutation.CreatedAt(); exists { + s.SetIgnore(compositemodelroute.FieldCreatedAt) + } + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.CompositeModelRoute.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *CompositeModelRouteUpsertBulk) Ignore() *CompositeModelRouteUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *CompositeModelRouteUpsertBulk) DoNothing() *CompositeModelRouteUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the CompositeModelRouteCreateBulk.OnConflict +// documentation for more info. +func (u *CompositeModelRouteUpsertBulk) Update(set func(*CompositeModelRouteUpsert)) *CompositeModelRouteUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&CompositeModelRouteUpsert{UpdateSet: update}) + })) + return u +} + +// SetUpdatedAt sets the "updated_at" field. +func (u *CompositeModelRouteUpsertBulk) SetUpdatedAt(v time.Time) *CompositeModelRouteUpsertBulk { + return u.Update(func(s *CompositeModelRouteUpsert) { + s.SetUpdatedAt(v) + }) +} + +// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create. +func (u *CompositeModelRouteUpsertBulk) UpdateUpdatedAt() *CompositeModelRouteUpsertBulk { + return u.Update(func(s *CompositeModelRouteUpsert) { + s.UpdateUpdatedAt() + }) +} + +// SetDeletedAt sets the "deleted_at" field. +func (u *CompositeModelRouteUpsertBulk) SetDeletedAt(v time.Time) *CompositeModelRouteUpsertBulk { + return u.Update(func(s *CompositeModelRouteUpsert) { + s.SetDeletedAt(v) + }) +} + +// UpdateDeletedAt sets the "deleted_at" field to the value that was provided on create. +func (u *CompositeModelRouteUpsertBulk) UpdateDeletedAt() *CompositeModelRouteUpsertBulk { + return u.Update(func(s *CompositeModelRouteUpsert) { + s.UpdateDeletedAt() + }) +} + +// ClearDeletedAt clears the value of the "deleted_at" field. +func (u *CompositeModelRouteUpsertBulk) ClearDeletedAt() *CompositeModelRouteUpsertBulk { + return u.Update(func(s *CompositeModelRouteUpsert) { + s.ClearDeletedAt() + }) +} + +// SetGroupID sets the "group_id" field. +func (u *CompositeModelRouteUpsertBulk) SetGroupID(v int64) *CompositeModelRouteUpsertBulk { + return u.Update(func(s *CompositeModelRouteUpsert) { + s.SetGroupID(v) + }) +} + +// UpdateGroupID sets the "group_id" field to the value that was provided on create. +func (u *CompositeModelRouteUpsertBulk) UpdateGroupID() *CompositeModelRouteUpsertBulk { + return u.Update(func(s *CompositeModelRouteUpsert) { + s.UpdateGroupID() + }) +} + +// SetPublicModel sets the "public_model" field. +func (u *CompositeModelRouteUpsertBulk) SetPublicModel(v string) *CompositeModelRouteUpsertBulk { + return u.Update(func(s *CompositeModelRouteUpsert) { + s.SetPublicModel(v) + }) +} + +// UpdatePublicModel sets the "public_model" field to the value that was provided on create. +func (u *CompositeModelRouteUpsertBulk) UpdatePublicModel() *CompositeModelRouteUpsertBulk { + return u.Update(func(s *CompositeModelRouteUpsert) { + s.UpdatePublicModel() + }) +} + +// SetMatchType sets the "match_type" field. +func (u *CompositeModelRouteUpsertBulk) SetMatchType(v string) *CompositeModelRouteUpsertBulk { + return u.Update(func(s *CompositeModelRouteUpsert) { + s.SetMatchType(v) + }) +} + +// UpdateMatchType sets the "match_type" field to the value that was provided on create. +func (u *CompositeModelRouteUpsertBulk) UpdateMatchType() *CompositeModelRouteUpsertBulk { + return u.Update(func(s *CompositeModelRouteUpsert) { + s.UpdateMatchType() + }) +} + +// SetTargetPlatform sets the "target_platform" field. +func (u *CompositeModelRouteUpsertBulk) SetTargetPlatform(v string) *CompositeModelRouteUpsertBulk { + return u.Update(func(s *CompositeModelRouteUpsert) { + s.SetTargetPlatform(v) + }) +} + +// UpdateTargetPlatform sets the "target_platform" field to the value that was provided on create. +func (u *CompositeModelRouteUpsertBulk) UpdateTargetPlatform() *CompositeModelRouteUpsertBulk { + return u.Update(func(s *CompositeModelRouteUpsert) { + s.UpdateTargetPlatform() + }) +} + +// SetUpstreamModel sets the "upstream_model" field. +func (u *CompositeModelRouteUpsertBulk) SetUpstreamModel(v string) *CompositeModelRouteUpsertBulk { + return u.Update(func(s *CompositeModelRouteUpsert) { + s.SetUpstreamModel(v) + }) +} + +// UpdateUpstreamModel sets the "upstream_model" field to the value that was provided on create. +func (u *CompositeModelRouteUpsertBulk) UpdateUpstreamModel() *CompositeModelRouteUpsertBulk { + return u.Update(func(s *CompositeModelRouteUpsert) { + s.UpdateUpstreamModel() + }) +} + +// SetEndpoint sets the "endpoint" field. +func (u *CompositeModelRouteUpsertBulk) SetEndpoint(v string) *CompositeModelRouteUpsertBulk { + return u.Update(func(s *CompositeModelRouteUpsert) { + s.SetEndpoint(v) + }) +} + +// UpdateEndpoint sets the "endpoint" field to the value that was provided on create. +func (u *CompositeModelRouteUpsertBulk) UpdateEndpoint() *CompositeModelRouteUpsertBulk { + return u.Update(func(s *CompositeModelRouteUpsert) { + s.UpdateEndpoint() + }) +} + +// SetPriority sets the "priority" field. +func (u *CompositeModelRouteUpsertBulk) SetPriority(v int) *CompositeModelRouteUpsertBulk { + return u.Update(func(s *CompositeModelRouteUpsert) { + s.SetPriority(v) + }) +} + +// AddPriority adds v to the "priority" field. +func (u *CompositeModelRouteUpsertBulk) AddPriority(v int) *CompositeModelRouteUpsertBulk { + return u.Update(func(s *CompositeModelRouteUpsert) { + s.AddPriority(v) + }) +} + +// UpdatePriority sets the "priority" field to the value that was provided on create. +func (u *CompositeModelRouteUpsertBulk) UpdatePriority() *CompositeModelRouteUpsertBulk { + return u.Update(func(s *CompositeModelRouteUpsert) { + s.UpdatePriority() + }) +} + +// SetEnabled sets the "enabled" field. +func (u *CompositeModelRouteUpsertBulk) SetEnabled(v bool) *CompositeModelRouteUpsertBulk { + return u.Update(func(s *CompositeModelRouteUpsert) { + s.SetEnabled(v) + }) +} + +// UpdateEnabled sets the "enabled" field to the value that was provided on create. +func (u *CompositeModelRouteUpsertBulk) UpdateEnabled() *CompositeModelRouteUpsertBulk { + return u.Update(func(s *CompositeModelRouteUpsert) { + s.UpdateEnabled() + }) +} + +// SetNotes sets the "notes" field. +func (u *CompositeModelRouteUpsertBulk) SetNotes(v string) *CompositeModelRouteUpsertBulk { + return u.Update(func(s *CompositeModelRouteUpsert) { + s.SetNotes(v) + }) +} + +// UpdateNotes sets the "notes" field to the value that was provided on create. +func (u *CompositeModelRouteUpsertBulk) UpdateNotes() *CompositeModelRouteUpsertBulk { + return u.Update(func(s *CompositeModelRouteUpsert) { + s.UpdateNotes() + }) +} + +// ClearNotes clears the value of the "notes" field. +func (u *CompositeModelRouteUpsertBulk) ClearNotes() *CompositeModelRouteUpsertBulk { + return u.Update(func(s *CompositeModelRouteUpsert) { + s.ClearNotes() + }) +} + +// Exec executes the query. +func (u *CompositeModelRouteUpsertBulk) Exec(ctx context.Context) error { + if u.create.err != nil { + return u.create.err + } + for i, b := range u.create.builders { + if len(b.conflict) != 0 { + return fmt.Errorf("ent: OnConflict was set for builder %d. Set it on the CompositeModelRouteCreateBulk instead", i) + } + } + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for CompositeModelRouteCreateBulk.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *CompositeModelRouteUpsertBulk) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/backend/ent/compositemodelroute_delete.go b/backend/ent/compositemodelroute_delete.go new file mode 100644 index 0000000000..0e5c398166 --- /dev/null +++ b/backend/ent/compositemodelroute_delete.go @@ -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) + } +} diff --git a/backend/ent/compositemodelroute_query.go b/backend/ent/compositemodelroute_query.go new file mode 100644 index 0000000000..0405dce257 --- /dev/null +++ b/backend/ent/compositemodelroute_query.go @@ -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) +} diff --git a/backend/ent/compositemodelroute_update.go b/backend/ent/compositemodelroute_update.go new file mode 100644 index 0000000000..c7ad6762d4 --- /dev/null +++ b/backend/ent/compositemodelroute_update.go @@ -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 +} diff --git a/backend/ent/ent.go b/backend/ent/ent.go index d23f61327f..29f3f0d68f 100644 --- a/backend/ent/ent.go +++ b/backend/ent/ent.go @@ -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, diff --git a/backend/ent/hook/hook.go b/backend/ent/hook/hook.go index 181f2f99db..f3beb5b644 100644 --- a/backend/ent/hook/hook.go +++ b/backend/ent/hook/hook.go @@ -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) diff --git a/backend/ent/intercept/intercept.go b/backend/ent/intercept/intercept.go index 7aeb07692d..5a6bd40cfe 100644 --- a/backend/ent/intercept/intercept.go +++ b/backend/ent/intercept/intercept.go @@ -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: diff --git a/backend/ent/migrate/schema.go b/backend/ent/migrate/schema.go index 9d979e4c21..d8600a87c6 100644 --- a/backend/ent/migrate/schema.go +++ b/backend/ent/migrate/schema.go @@ -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", } diff --git a/backend/ent/mutation.go b/backend/ent/mutation.go index 1e69df30fc..d0633e5425 100644 --- a/backend/ent/mutation.go +++ b/backend/ent/mutation.go @@ -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" @@ -77,6 +78,7 @@ const ( TypeChannelMonitorDailyRollup = "ChannelMonitorDailyRollup" TypeChannelMonitorHistory = "ChannelMonitorHistory" TypeChannelMonitorRequestTemplate = "ChannelMonitorRequestTemplate" + TypeCompositeModelRoute = "CompositeModelRoute" TypeErrorPassthroughRule = "ErrorPassthroughRule" TypeGroup = "Group" TypeIdempotencyRecord = "IdempotencyRecord" @@ -19468,6 +19470,1057 @@ func (m *ChannelMonitorRequestTemplateMutation) ResetEdge(name string) error { return fmt.Errorf("unknown ChannelMonitorRequestTemplate edge %s", name) } +// CompositeModelRouteMutation represents an operation that mutates the CompositeModelRoute nodes in the graph. +type CompositeModelRouteMutation struct { + config + op Op + typ string + id *int64 + created_at *time.Time + updated_at *time.Time + deleted_at *time.Time + public_model *string + match_type *string + target_platform *string + upstream_model *string + endpoint *string + priority *int + addpriority *int + enabled *bool + notes *string + clearedFields map[string]struct{} + group *int64 + clearedgroup bool + done bool + oldValue func(context.Context) (*CompositeModelRoute, error) + predicates []predicate.CompositeModelRoute +} + +var _ ent.Mutation = (*CompositeModelRouteMutation)(nil) + +// compositemodelrouteOption allows management of the mutation configuration using functional options. +type compositemodelrouteOption func(*CompositeModelRouteMutation) + +// newCompositeModelRouteMutation creates new mutation for the CompositeModelRoute entity. +func newCompositeModelRouteMutation(c config, op Op, opts ...compositemodelrouteOption) *CompositeModelRouteMutation { + m := &CompositeModelRouteMutation{ + config: c, + op: op, + typ: TypeCompositeModelRoute, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withCompositeModelRouteID sets the ID field of the mutation. +func withCompositeModelRouteID(id int64) compositemodelrouteOption { + return func(m *CompositeModelRouteMutation) { + var ( + err error + once sync.Once + value *CompositeModelRoute + ) + m.oldValue = func(ctx context.Context) (*CompositeModelRoute, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().CompositeModelRoute.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withCompositeModelRoute sets the old CompositeModelRoute of the mutation. +func withCompositeModelRoute(node *CompositeModelRoute) compositemodelrouteOption { + return func(m *CompositeModelRouteMutation) { + m.oldValue = func(context.Context) (*CompositeModelRoute, error) { + return node, nil + } + m.id = &node.ID + } +} + +// Client returns a new `ent.Client` from the mutation. If the mutation was +// executed in a transaction (ent.Tx), a transactional client is returned. +func (m CompositeModelRouteMutation) Client() *Client { + client := &Client{config: m.config} + client.init() + return client +} + +// Tx returns an `ent.Tx` for mutations that were executed in transactions; +// it returns an error otherwise. +func (m CompositeModelRouteMutation) Tx() (*Tx, error) { + if _, ok := m.driver.(*txDriver); !ok { + return nil, errors.New("ent: mutation is not running in a transaction") + } + tx := &Tx{config: m.config} + tx.init() + return tx, nil +} + +// ID returns the ID value in the mutation. Note that the ID is only available +// if it was provided to the builder or after it was returned from the database. +func (m *CompositeModelRouteMutation) ID() (id int64, exists bool) { + if m.id == nil { + return + } + return *m.id, true +} + +// IDs queries the database and returns the entity ids that match the mutation's predicate. +// That means, if the mutation is applied within a transaction with an isolation level such +// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated +// or updated by the mutation. +func (m *CompositeModelRouteMutation) IDs(ctx context.Context) ([]int64, error) { + switch { + case m.op.Is(OpUpdateOne | OpDeleteOne): + id, exists := m.ID() + if exists { + return []int64{id}, nil + } + fallthrough + case m.op.Is(OpUpdate | OpDelete): + return m.Client().CompositeModelRoute.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetCreatedAt sets the "created_at" field. +func (m *CompositeModelRouteMutation) SetCreatedAt(t time.Time) { + m.created_at = &t +} + +// CreatedAt returns the value of the "created_at" field in the mutation. +func (m *CompositeModelRouteMutation) CreatedAt() (r time.Time, exists bool) { + v := m.created_at + if v == nil { + return + } + return *v, true +} + +// OldCreatedAt returns the old "created_at" field's value of the CompositeModelRoute entity. +// If the CompositeModelRoute object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *CompositeModelRouteMutation) OldCreatedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreatedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreatedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreatedAt: %w", err) + } + return oldValue.CreatedAt, nil +} + +// ResetCreatedAt resets all changes to the "created_at" field. +func (m *CompositeModelRouteMutation) ResetCreatedAt() { + m.created_at = nil +} + +// SetUpdatedAt sets the "updated_at" field. +func (m *CompositeModelRouteMutation) SetUpdatedAt(t time.Time) { + m.updated_at = &t +} + +// UpdatedAt returns the value of the "updated_at" field in the mutation. +func (m *CompositeModelRouteMutation) UpdatedAt() (r time.Time, exists bool) { + v := m.updated_at + if v == nil { + return + } + return *v, true +} + +// OldUpdatedAt returns the old "updated_at" field's value of the CompositeModelRoute entity. +// If the CompositeModelRoute object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *CompositeModelRouteMutation) OldUpdatedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldUpdatedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldUpdatedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldUpdatedAt: %w", err) + } + return oldValue.UpdatedAt, nil +} + +// ResetUpdatedAt resets all changes to the "updated_at" field. +func (m *CompositeModelRouteMutation) ResetUpdatedAt() { + m.updated_at = nil +} + +// SetDeletedAt sets the "deleted_at" field. +func (m *CompositeModelRouteMutation) SetDeletedAt(t time.Time) { + m.deleted_at = &t +} + +// DeletedAt returns the value of the "deleted_at" field in the mutation. +func (m *CompositeModelRouteMutation) DeletedAt() (r time.Time, exists bool) { + v := m.deleted_at + if v == nil { + return + } + return *v, true +} + +// OldDeletedAt returns the old "deleted_at" field's value of the CompositeModelRoute entity. +// If the CompositeModelRoute object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *CompositeModelRouteMutation) OldDeletedAt(ctx context.Context) (v *time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldDeletedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldDeletedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldDeletedAt: %w", err) + } + return oldValue.DeletedAt, nil +} + +// ClearDeletedAt clears the value of the "deleted_at" field. +func (m *CompositeModelRouteMutation) ClearDeletedAt() { + m.deleted_at = nil + m.clearedFields[compositemodelroute.FieldDeletedAt] = struct{}{} +} + +// DeletedAtCleared returns if the "deleted_at" field was cleared in this mutation. +func (m *CompositeModelRouteMutation) DeletedAtCleared() bool { + _, ok := m.clearedFields[compositemodelroute.FieldDeletedAt] + return ok +} + +// ResetDeletedAt resets all changes to the "deleted_at" field. +func (m *CompositeModelRouteMutation) ResetDeletedAt() { + m.deleted_at = nil + delete(m.clearedFields, compositemodelroute.FieldDeletedAt) +} + +// SetGroupID sets the "group_id" field. +func (m *CompositeModelRouteMutation) SetGroupID(i int64) { + m.group = &i +} + +// GroupID returns the value of the "group_id" field in the mutation. +func (m *CompositeModelRouteMutation) GroupID() (r int64, exists bool) { + v := m.group + if v == nil { + return + } + return *v, true +} + +// OldGroupID returns the old "group_id" field's value of the CompositeModelRoute entity. +// If the CompositeModelRoute object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *CompositeModelRouteMutation) OldGroupID(ctx context.Context) (v int64, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldGroupID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldGroupID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldGroupID: %w", err) + } + return oldValue.GroupID, nil +} + +// ResetGroupID resets all changes to the "group_id" field. +func (m *CompositeModelRouteMutation) ResetGroupID() { + m.group = nil +} + +// SetPublicModel sets the "public_model" field. +func (m *CompositeModelRouteMutation) SetPublicModel(s string) { + m.public_model = &s +} + +// PublicModel returns the value of the "public_model" field in the mutation. +func (m *CompositeModelRouteMutation) PublicModel() (r string, exists bool) { + v := m.public_model + if v == nil { + return + } + return *v, true +} + +// OldPublicModel returns the old "public_model" field's value of the CompositeModelRoute entity. +// If the CompositeModelRoute object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *CompositeModelRouteMutation) OldPublicModel(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldPublicModel is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldPublicModel requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldPublicModel: %w", err) + } + return oldValue.PublicModel, nil +} + +// ResetPublicModel resets all changes to the "public_model" field. +func (m *CompositeModelRouteMutation) ResetPublicModel() { + m.public_model = nil +} + +// SetMatchType sets the "match_type" field. +func (m *CompositeModelRouteMutation) SetMatchType(s string) { + m.match_type = &s +} + +// MatchType returns the value of the "match_type" field in the mutation. +func (m *CompositeModelRouteMutation) MatchType() (r string, exists bool) { + v := m.match_type + if v == nil { + return + } + return *v, true +} + +// OldMatchType returns the old "match_type" field's value of the CompositeModelRoute entity. +// If the CompositeModelRoute object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *CompositeModelRouteMutation) OldMatchType(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldMatchType is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldMatchType requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldMatchType: %w", err) + } + return oldValue.MatchType, nil +} + +// ResetMatchType resets all changes to the "match_type" field. +func (m *CompositeModelRouteMutation) ResetMatchType() { + m.match_type = nil +} + +// SetTargetPlatform sets the "target_platform" field. +func (m *CompositeModelRouteMutation) SetTargetPlatform(s string) { + m.target_platform = &s +} + +// TargetPlatform returns the value of the "target_platform" field in the mutation. +func (m *CompositeModelRouteMutation) TargetPlatform() (r string, exists bool) { + v := m.target_platform + if v == nil { + return + } + return *v, true +} + +// OldTargetPlatform returns the old "target_platform" field's value of the CompositeModelRoute entity. +// If the CompositeModelRoute object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *CompositeModelRouteMutation) OldTargetPlatform(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldTargetPlatform is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldTargetPlatform requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldTargetPlatform: %w", err) + } + return oldValue.TargetPlatform, nil +} + +// ResetTargetPlatform resets all changes to the "target_platform" field. +func (m *CompositeModelRouteMutation) ResetTargetPlatform() { + m.target_platform = nil +} + +// SetUpstreamModel sets the "upstream_model" field. +func (m *CompositeModelRouteMutation) SetUpstreamModel(s string) { + m.upstream_model = &s +} + +// UpstreamModel returns the value of the "upstream_model" field in the mutation. +func (m *CompositeModelRouteMutation) UpstreamModel() (r string, exists bool) { + v := m.upstream_model + if v == nil { + return + } + return *v, true +} + +// OldUpstreamModel returns the old "upstream_model" field's value of the CompositeModelRoute entity. +// If the CompositeModelRoute object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *CompositeModelRouteMutation) OldUpstreamModel(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldUpstreamModel is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldUpstreamModel requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldUpstreamModel: %w", err) + } + return oldValue.UpstreamModel, nil +} + +// ResetUpstreamModel resets all changes to the "upstream_model" field. +func (m *CompositeModelRouteMutation) ResetUpstreamModel() { + m.upstream_model = nil +} + +// SetEndpoint sets the "endpoint" field. +func (m *CompositeModelRouteMutation) SetEndpoint(s string) { + m.endpoint = &s +} + +// Endpoint returns the value of the "endpoint" field in the mutation. +func (m *CompositeModelRouteMutation) Endpoint() (r string, exists bool) { + v := m.endpoint + if v == nil { + return + } + return *v, true +} + +// OldEndpoint returns the old "endpoint" field's value of the CompositeModelRoute entity. +// If the CompositeModelRoute object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *CompositeModelRouteMutation) OldEndpoint(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldEndpoint is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldEndpoint requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldEndpoint: %w", err) + } + return oldValue.Endpoint, nil +} + +// ResetEndpoint resets all changes to the "endpoint" field. +func (m *CompositeModelRouteMutation) ResetEndpoint() { + m.endpoint = nil +} + +// SetPriority sets the "priority" field. +func (m *CompositeModelRouteMutation) SetPriority(i int) { + m.priority = &i + m.addpriority = nil +} + +// Priority returns the value of the "priority" field in the mutation. +func (m *CompositeModelRouteMutation) Priority() (r int, exists bool) { + v := m.priority + if v == nil { + return + } + return *v, true +} + +// OldPriority returns the old "priority" field's value of the CompositeModelRoute entity. +// If the CompositeModelRoute object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *CompositeModelRouteMutation) OldPriority(ctx context.Context) (v int, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldPriority is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldPriority requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldPriority: %w", err) + } + return oldValue.Priority, nil +} + +// AddPriority adds i to the "priority" field. +func (m *CompositeModelRouteMutation) AddPriority(i int) { + if m.addpriority != nil { + *m.addpriority += i + } else { + m.addpriority = &i + } +} + +// AddedPriority returns the value that was added to the "priority" field in this mutation. +func (m *CompositeModelRouteMutation) AddedPriority() (r int, exists bool) { + v := m.addpriority + if v == nil { + return + } + return *v, true +} + +// ResetPriority resets all changes to the "priority" field. +func (m *CompositeModelRouteMutation) ResetPriority() { + m.priority = nil + m.addpriority = nil +} + +// SetEnabled sets the "enabled" field. +func (m *CompositeModelRouteMutation) SetEnabled(b bool) { + m.enabled = &b +} + +// Enabled returns the value of the "enabled" field in the mutation. +func (m *CompositeModelRouteMutation) Enabled() (r bool, exists bool) { + v := m.enabled + if v == nil { + return + } + return *v, true +} + +// OldEnabled returns the old "enabled" field's value of the CompositeModelRoute entity. +// If the CompositeModelRoute object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *CompositeModelRouteMutation) OldEnabled(ctx context.Context) (v bool, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldEnabled is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldEnabled requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldEnabled: %w", err) + } + return oldValue.Enabled, nil +} + +// ResetEnabled resets all changes to the "enabled" field. +func (m *CompositeModelRouteMutation) ResetEnabled() { + m.enabled = nil +} + +// SetNotes sets the "notes" field. +func (m *CompositeModelRouteMutation) SetNotes(s string) { + m.notes = &s +} + +// Notes returns the value of the "notes" field in the mutation. +func (m *CompositeModelRouteMutation) Notes() (r string, exists bool) { + v := m.notes + if v == nil { + return + } + return *v, true +} + +// OldNotes returns the old "notes" field's value of the CompositeModelRoute entity. +// If the CompositeModelRoute object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *CompositeModelRouteMutation) OldNotes(ctx context.Context) (v *string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldNotes is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldNotes requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldNotes: %w", err) + } + return oldValue.Notes, nil +} + +// ClearNotes clears the value of the "notes" field. +func (m *CompositeModelRouteMutation) ClearNotes() { + m.notes = nil + m.clearedFields[compositemodelroute.FieldNotes] = struct{}{} +} + +// NotesCleared returns if the "notes" field was cleared in this mutation. +func (m *CompositeModelRouteMutation) NotesCleared() bool { + _, ok := m.clearedFields[compositemodelroute.FieldNotes] + return ok +} + +// ResetNotes resets all changes to the "notes" field. +func (m *CompositeModelRouteMutation) ResetNotes() { + m.notes = nil + delete(m.clearedFields, compositemodelroute.FieldNotes) +} + +// ClearGroup clears the "group" edge to the Group entity. +func (m *CompositeModelRouteMutation) ClearGroup() { + m.clearedgroup = true + m.clearedFields[compositemodelroute.FieldGroupID] = struct{}{} +} + +// GroupCleared reports if the "group" edge to the Group entity was cleared. +func (m *CompositeModelRouteMutation) GroupCleared() bool { + return m.clearedgroup +} + +// GroupIDs returns the "group" edge IDs in the mutation. +// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use +// GroupID instead. It exists only for internal usage by the builders. +func (m *CompositeModelRouteMutation) GroupIDs() (ids []int64) { + if id := m.group; id != nil { + ids = append(ids, *id) + } + return +} + +// ResetGroup resets all changes to the "group" edge. +func (m *CompositeModelRouteMutation) ResetGroup() { + m.group = nil + m.clearedgroup = false +} + +// Where appends a list predicates to the CompositeModelRouteMutation builder. +func (m *CompositeModelRouteMutation) Where(ps ...predicate.CompositeModelRoute) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the CompositeModelRouteMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *CompositeModelRouteMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.CompositeModelRoute, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *CompositeModelRouteMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *CompositeModelRouteMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (CompositeModelRoute). +func (m *CompositeModelRouteMutation) Type() string { + return m.typ +} + +// Fields returns all fields that were changed during this mutation. Note that in +// order to get all numeric fields that were incremented/decremented, call +// AddedFields(). +func (m *CompositeModelRouteMutation) Fields() []string { + fields := make([]string, 0, 12) + if m.created_at != nil { + fields = append(fields, compositemodelroute.FieldCreatedAt) + } + if m.updated_at != nil { + fields = append(fields, compositemodelroute.FieldUpdatedAt) + } + if m.deleted_at != nil { + fields = append(fields, compositemodelroute.FieldDeletedAt) + } + if m.group != nil { + fields = append(fields, compositemodelroute.FieldGroupID) + } + if m.public_model != nil { + fields = append(fields, compositemodelroute.FieldPublicModel) + } + if m.match_type != nil { + fields = append(fields, compositemodelroute.FieldMatchType) + } + if m.target_platform != nil { + fields = append(fields, compositemodelroute.FieldTargetPlatform) + } + if m.upstream_model != nil { + fields = append(fields, compositemodelroute.FieldUpstreamModel) + } + if m.endpoint != nil { + fields = append(fields, compositemodelroute.FieldEndpoint) + } + if m.priority != nil { + fields = append(fields, compositemodelroute.FieldPriority) + } + if m.enabled != nil { + fields = append(fields, compositemodelroute.FieldEnabled) + } + if m.notes != nil { + fields = append(fields, compositemodelroute.FieldNotes) + } + return fields +} + +// Field returns the value of a field with the given name. The second boolean +// return value indicates that this field was not set, or was not defined in the +// schema. +func (m *CompositeModelRouteMutation) Field(name string) (ent.Value, bool) { + switch name { + case compositemodelroute.FieldCreatedAt: + return m.CreatedAt() + case compositemodelroute.FieldUpdatedAt: + return m.UpdatedAt() + case compositemodelroute.FieldDeletedAt: + return m.DeletedAt() + case compositemodelroute.FieldGroupID: + return m.GroupID() + case compositemodelroute.FieldPublicModel: + return m.PublicModel() + case compositemodelroute.FieldMatchType: + return m.MatchType() + case compositemodelroute.FieldTargetPlatform: + return m.TargetPlatform() + case compositemodelroute.FieldUpstreamModel: + return m.UpstreamModel() + case compositemodelroute.FieldEndpoint: + return m.Endpoint() + case compositemodelroute.FieldPriority: + return m.Priority() + case compositemodelroute.FieldEnabled: + return m.Enabled() + case compositemodelroute.FieldNotes: + return m.Notes() + } + return nil, false +} + +// OldField returns the old value of the field from the database. An error is +// returned if the mutation operation is not UpdateOne, or the query to the +// database failed. +func (m *CompositeModelRouteMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case compositemodelroute.FieldCreatedAt: + return m.OldCreatedAt(ctx) + case compositemodelroute.FieldUpdatedAt: + return m.OldUpdatedAt(ctx) + case compositemodelroute.FieldDeletedAt: + return m.OldDeletedAt(ctx) + case compositemodelroute.FieldGroupID: + return m.OldGroupID(ctx) + case compositemodelroute.FieldPublicModel: + return m.OldPublicModel(ctx) + case compositemodelroute.FieldMatchType: + return m.OldMatchType(ctx) + case compositemodelroute.FieldTargetPlatform: + return m.OldTargetPlatform(ctx) + case compositemodelroute.FieldUpstreamModel: + return m.OldUpstreamModel(ctx) + case compositemodelroute.FieldEndpoint: + return m.OldEndpoint(ctx) + case compositemodelroute.FieldPriority: + return m.OldPriority(ctx) + case compositemodelroute.FieldEnabled: + return m.OldEnabled(ctx) + case compositemodelroute.FieldNotes: + return m.OldNotes(ctx) + } + return nil, fmt.Errorf("unknown CompositeModelRoute field %s", name) +} + +// SetField sets the value of a field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *CompositeModelRouteMutation) SetField(name string, value ent.Value) error { + switch name { + case compositemodelroute.FieldCreatedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreatedAt(v) + return nil + case compositemodelroute.FieldUpdatedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUpdatedAt(v) + return nil + case compositemodelroute.FieldDeletedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetDeletedAt(v) + return nil + case compositemodelroute.FieldGroupID: + v, ok := value.(int64) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetGroupID(v) + return nil + case compositemodelroute.FieldPublicModel: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetPublicModel(v) + return nil + case compositemodelroute.FieldMatchType: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetMatchType(v) + return nil + case compositemodelroute.FieldTargetPlatform: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetTargetPlatform(v) + return nil + case compositemodelroute.FieldUpstreamModel: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUpstreamModel(v) + return nil + case compositemodelroute.FieldEndpoint: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetEndpoint(v) + return nil + case compositemodelroute.FieldPriority: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetPriority(v) + return nil + case compositemodelroute.FieldEnabled: + v, ok := value.(bool) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetEnabled(v) + return nil + case compositemodelroute.FieldNotes: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetNotes(v) + return nil + } + return fmt.Errorf("unknown CompositeModelRoute field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *CompositeModelRouteMutation) AddedFields() []string { + var fields []string + if m.addpriority != nil { + fields = append(fields, compositemodelroute.FieldPriority) + } + return fields +} + +// AddedField returns the numeric value that was incremented/decremented on a field +// with the given name. The second boolean return value indicates that this field +// was not set, or was not defined in the schema. +func (m *CompositeModelRouteMutation) AddedField(name string) (ent.Value, bool) { + switch name { + case compositemodelroute.FieldPriority: + return m.AddedPriority() + } + return nil, false +} + +// AddField adds the value to the field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *CompositeModelRouteMutation) AddField(name string, value ent.Value) error { + switch name { + case compositemodelroute.FieldPriority: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.AddPriority(v) + return nil + } + return fmt.Errorf("unknown CompositeModelRoute numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *CompositeModelRouteMutation) ClearedFields() []string { + var fields []string + if m.FieldCleared(compositemodelroute.FieldDeletedAt) { + fields = append(fields, compositemodelroute.FieldDeletedAt) + } + if m.FieldCleared(compositemodelroute.FieldNotes) { + fields = append(fields, compositemodelroute.FieldNotes) + } + return fields +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *CompositeModelRouteMutation) FieldCleared(name string) bool { + _, ok := m.clearedFields[name] + return ok +} + +// ClearField clears the value of the field with the given name. It returns an +// error if the field is not defined in the schema. +func (m *CompositeModelRouteMutation) ClearField(name string) error { + switch name { + case compositemodelroute.FieldDeletedAt: + m.ClearDeletedAt() + return nil + case compositemodelroute.FieldNotes: + m.ClearNotes() + return nil + } + return fmt.Errorf("unknown CompositeModelRoute nullable field %s", name) +} + +// ResetField resets all changes in the mutation for the field with the given name. +// It returns an error if the field is not defined in the schema. +func (m *CompositeModelRouteMutation) ResetField(name string) error { + switch name { + case compositemodelroute.FieldCreatedAt: + m.ResetCreatedAt() + return nil + case compositemodelroute.FieldUpdatedAt: + m.ResetUpdatedAt() + return nil + case compositemodelroute.FieldDeletedAt: + m.ResetDeletedAt() + return nil + case compositemodelroute.FieldGroupID: + m.ResetGroupID() + return nil + case compositemodelroute.FieldPublicModel: + m.ResetPublicModel() + return nil + case compositemodelroute.FieldMatchType: + m.ResetMatchType() + return nil + case compositemodelroute.FieldTargetPlatform: + m.ResetTargetPlatform() + return nil + case compositemodelroute.FieldUpstreamModel: + m.ResetUpstreamModel() + return nil + case compositemodelroute.FieldEndpoint: + m.ResetEndpoint() + return nil + case compositemodelroute.FieldPriority: + m.ResetPriority() + return nil + case compositemodelroute.FieldEnabled: + m.ResetEnabled() + return nil + case compositemodelroute.FieldNotes: + m.ResetNotes() + return nil + } + return fmt.Errorf("unknown CompositeModelRoute field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *CompositeModelRouteMutation) AddedEdges() []string { + edges := make([]string, 0, 1) + if m.group != nil { + edges = append(edges, compositemodelroute.EdgeGroup) + } + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *CompositeModelRouteMutation) AddedIDs(name string) []ent.Value { + switch name { + case compositemodelroute.EdgeGroup: + if id := m.group; id != nil { + return []ent.Value{*id} + } + } + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *CompositeModelRouteMutation) RemovedEdges() []string { + edges := make([]string, 0, 1) + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *CompositeModelRouteMutation) RemovedIDs(name string) []ent.Value { + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *CompositeModelRouteMutation) ClearedEdges() []string { + edges := make([]string, 0, 1) + if m.clearedgroup { + edges = append(edges, compositemodelroute.EdgeGroup) + } + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *CompositeModelRouteMutation) EdgeCleared(name string) bool { + switch name { + case compositemodelroute.EdgeGroup: + return m.clearedgroup + } + return false +} + +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *CompositeModelRouteMutation) ClearEdge(name string) error { + switch name { + case compositemodelroute.EdgeGroup: + m.ClearGroup() + return nil + } + return fmt.Errorf("unknown CompositeModelRoute unique edge %s", name) +} + +// ResetEdge resets all changes to the edge with the given name in this mutation. +// It returns an error if the edge is not defined in the schema. +func (m *CompositeModelRouteMutation) ResetEdge(name string) error { + switch name { + case compositemodelroute.EdgeGroup: + m.ResetGroup() + return nil + } + return fmt.Errorf("unknown CompositeModelRoute edge %s", name) +} + // ErrorPassthroughRuleMutation represents an operation that mutates the ErrorPassthroughRule nodes in the graph. type ErrorPassthroughRuleMutation struct { config diff --git a/backend/ent/predicate/predicate.go b/backend/ent/predicate/predicate.go index 8d18d38151..f8b33fe366 100644 --- a/backend/ent/predicate/predicate.go +++ b/backend/ent/predicate/predicate.go @@ -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) diff --git a/backend/ent/runtime/runtime.go b/backend/ent/runtime/runtime.go index b1dcc8c585..9e5a0119e3 100644 --- a/backend/ent/runtime/runtime.go +++ b/backend/ent/runtime/runtime.go @@ -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 diff --git a/backend/ent/schema/composite_model_route.go b/backend/ent/schema/composite_model_route.go new file mode 100644 index 0000000000..82b5bb274b --- /dev/null +++ b/backend/ent/schema/composite_model_route.go @@ -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"), + } +} diff --git a/backend/ent/tx.go b/backend/ent/tx.go index 6de2c2b63b..f028840340 100644 --- a/backend/ent/tx.go +++ b/backend/ent/tx.go @@ -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) diff --git a/backend/internal/handler/admin/admin_basic_handlers_test.go b/backend/internal/handler/admin/admin_basic_handlers_test.go index 6c05d7bf5e..fec393314b 100644 --- a/backend/internal/handler/admin/admin_basic_handlers_test.go +++ b/backend/internal/handler/admin/admin_basic_handlers_test.go @@ -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") diff --git a/backend/internal/handler/admin/admin_service_stub_test.go b/backend/internal/handler/admin/admin_service_stub_test.go index 035e171c9b..0eb661b932 100644 --- a/backend/internal/handler/admin/admin_service_stub_test.go +++ b/backend/internal/handler/admin/admin_service_stub_test.go @@ -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 diff --git a/backend/internal/handler/admin/group_handler.go b/backend/internal/handler/admin/group_handler.go index 63b7d83616..070582663f 100644 --- a/backend/internal/handler/admin/group_handler.go +++ b/backend/internal/handler/admin/group_handler.go @@ -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 diff --git a/backend/internal/handler/gateway_handler_warmup_intercept_unit_test.go b/backend/internal/handler/gateway_handler_warmup_intercept_unit_test.go index 8f3362f919..fac77eeb15 100644 --- a/backend/internal/handler/gateway_handler_warmup_intercept_unit_test.go +++ b/backend/internal/handler/gateway_handler_warmup_intercept_unit_test.go @@ -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 ) diff --git a/backend/internal/handler/gateway_models_test.go b/backend/internal/handler/gateway_models_test.go index 72fe837f70..af1bc5aea6 100644 --- a/backend/internal/handler/gateway_models_test.go +++ b/backend/internal/handler/gateway_models_test.go @@ -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, ), } } diff --git a/backend/internal/handler/gemini_v1beta_handler.go b/backend/internal/handler/gemini_v1beta_handler.go index 9a042f0e91..557bec3f9f 100644 --- a/backend/internal/handler/gemini_v1beta_handler.go +++ b/backend/internal/handler/gemini_v1beta_handler.go @@ -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)) diff --git a/backend/internal/handler/ops_error_logger.go b/backend/internal/handler/ops_error_logger.go index 5eb595f2ba..8be450b16f 100644 --- a/backend/internal/handler/ops_error_logger.go +++ b/backend/internal/handler/ops_error_logger.go @@ -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 == "" { diff --git a/backend/internal/pkg/ctxkey/ctxkey.go b/backend/internal/pkg/ctxkey/ctxkey.go index 064b70bdc8..2ec3e0fdbe 100644 --- a/backend/internal/pkg/ctxkey/ctxkey.go +++ b/backend/internal/pkg/ctxkey/ctxkey.go @@ -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" diff --git a/backend/internal/repository/composite_model_route_repo.go b/backend/internal/repository/composite_model_route_repo.go new file mode 100644 index 0000000000..53aab3022d --- /dev/null +++ b/backend/internal/repository/composite_model_route_repo.go @@ -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, + } +} diff --git a/backend/internal/repository/group_repo.go b/backend/internal/repository/group_repo.go index 2b770ffd7f..96be19a70c 100644 --- a/backend/internal/repository/group_repo.go +++ b/backend/internal/repository/group_repo.go @@ -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 } diff --git a/backend/internal/repository/wire.go b/backend/internal/repository/wire.go index 1ee3194ad6..66588299ba 100644 --- a/backend/internal/repository/wire.go +++ b/backend/internal/repository/wire.go @@ -69,6 +69,7 @@ var ProviderSet = wire.NewSet( NewAPIKeyRepository, NewGroupRepository, NewAdminGroupRepository, + NewCompositeModelRouteRepository, NewAccountRepository, NewAdminAccountRepository, NewScheduledTestPlanRepository, // 定时测试计划仓储 diff --git a/backend/internal/server/api_contract_test.go b/backend/internal/server/api_contract_test.go index 99c7c42328..c8b83684af 100644 --- a/backend/internal/server/api_contract_test.go +++ b/backend/internal/server/api_contract_test.go @@ -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) diff --git a/backend/internal/server/http.go b/backend/internal/server/http.go index fef7cd056e..4f6bfced17 100644 --- a/backend/internal/server/http.go +++ b/backend/internal/server/http.go @@ -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) { diff --git a/backend/internal/server/router.go b/backend/internal/server/router.go index e10672d83e..ea3600c3f6 100644 --- a/backend/internal/server/router.go +++ b/backend/internal/server/router.go @@ -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) diff --git a/backend/internal/server/routes/admin.go b/backend/internal/server/routes/admin.go index 7bc8afd7bb..b64ec64b56 100644 --- a/backend/internal/server/routes/admin.go +++ b/backend/internal/server/routes/admin.go @@ -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) diff --git a/backend/internal/server/routes/composite_platform_test.go b/backend/internal/server/routes/composite_platform_test.go index 4f6ce5f430..7b7e5f8c8d 100644 --- a/backend/internal/server/routes/composite_platform_test.go +++ b/backend/internal/server/routes/composite_platform_test.go @@ -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) +} diff --git a/backend/internal/server/routes/gateway.go b/backend/internal/server/routes/gateway.go index 070e32c077..b61d5d5e3f 100644 --- a/backend/internal/server/routes/gateway.go +++ b/backend/internal/server/routes/gateway.go @@ -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 + } +} diff --git a/backend/internal/server/routes/gateway_test.go b/backend/internal/server/routes/gateway_test.go index 43a43ff787..36c5d1158e 100644 --- a/backend/internal/server/routes/gateway_test.go +++ b/backend/internal/server/routes/gateway_test.go @@ -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) diff --git a/backend/internal/service/admin_group.go b/backend/internal/service/admin_group.go index d2e54e31b6..fdba370ea0 100644 --- a/backend/internal/service/admin_group.go +++ b/backend/internal/service/admin_group.go @@ -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: diff --git a/backend/internal/service/admin_service.go b/backend/internal/service/admin_service.go index b6ad7b5279..49c5272ed0 100644 --- a/backend/internal/service/admin_service.go +++ b/backend/internal/service/admin_service.go @@ -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, } } diff --git a/backend/internal/service/admin_service_group_test.go b/backend/internal/service/admin_service_group_test.go index 5e3ec23fb8..cf13417879 100644 --- a/backend/internal/service/admin_service_group_test.go +++ b/backend/internal/service/admin_service_group_test.go @@ -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) +} diff --git a/backend/internal/service/composite_model_route.go b/backend/internal/service/composite_model_route.go new file mode 100644 index 0000000000..2bae5c62df --- /dev/null +++ b/backend/internal/service/composite_model_route.go @@ -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 +} diff --git a/backend/internal/service/composite_platform.go b/backend/internal/service/composite_platform.go index 137c2f9286..e3b5f9fd99 100644 --- a/backend/internal/service/composite_platform.go +++ b/backend/internal/service/composite_platform.go @@ -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 diff --git a/backend/internal/service/composite_platform_test.go b/backend/internal/service/composite_platform_test.go index e127f95d44..290fc4ac0e 100644 --- a/backend/internal/service/composite_platform_test.go +++ b/backend/internal/service/composite_platform_test.go @@ -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)) } diff --git a/backend/internal/service/composite_route_resolver.go b/backend/internal/service/composite_route_resolver.go new file mode 100644 index 0000000000..3d69f13553 --- /dev/null +++ b/backend/internal/service/composite_route_resolver.go @@ -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 +} diff --git a/backend/internal/service/composite_route_resolver_test.go b/backend/internal/service/composite_route_resolver_test.go new file mode 100644 index 0000000000..4364c4ddcf --- /dev/null +++ b/backend/internal/service/composite_route_resolver_test.go @@ -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) +} diff --git a/backend/internal/service/gateway_record_usage_test.go b/backend/internal/service/gateway_record_usage_test.go index 2769251820..28963a4306 100644 --- a/backend/internal/service/gateway_record_usage_test.go +++ b/backend/internal/service/gateway_record_usage_test.go @@ -44,6 +44,7 @@ func newGatewayRecordUsageServiceForTest(usageRepo UsageLogRepository, userRepo nil, nil, nil, + nil, nil, // userPlatformQuotaRepo ) } diff --git a/backend/internal/service/gateway_scheduling.go b/backend/internal/service/gateway_scheduling.go index c876acf845..bda206f5e2 100644 --- a/backend/internal/service/gateway_scheduling.go +++ b/backend/internal/service/gateway_scheduling.go @@ -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 } diff --git a/backend/internal/service/gateway_service.go b/backend/internal/service/gateway_service.go index 23684af4aa..ad0150ec7f 100644 --- a/backend/internal/service/gateway_service.go +++ b/backend/internal/service/gateway_service.go @@ -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, } diff --git a/backend/internal/service/wire.go b/backend/internal/service/wire.go index 913cb6ba78..166c1ff832 100644 --- a/backend/internal/service/wire.go +++ b/backend/internal/service/wire.go @@ -681,6 +681,7 @@ var ProviderSet = wire.NewSet( ProvideAPIKeyAuthCacheInvalidator, ProvideAuthCacheInvalidationWorker, NewGroupService, + NewCompositeRouteResolver, NewAccountService, NewProxyService, NewRedeemService, diff --git a/backend/migrations/172_composite_model_routes.sql b/backend/migrations/172_composite_model_routes.sql new file mode 100644 index 0000000000..279eef4d1f --- /dev/null +++ b/backend/migrations/172_composite_model_routes.sql @@ -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; diff --git a/docs/COMPOSITE_GROUPS.md b/docs/COMPOSITE_GROUPS.md index f8397c0faf..4798ae0013 100644 --- a/docs/COMPOSITE_GROUPS.md +++ b/docs/COMPOSITE_GROUPS.md @@ -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. diff --git a/frontend/src/api/admin/groups.ts b/frontend/src/api/admin/groups.ts index 3d2d121fb0..0e69be4994 100644 --- a/frontend/src/api/admin/groups.ts +++ b/frontend/src/api/admin/groups.ts @@ -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 { + const { data } = await apiClient.get(`/admin/groups/${id}/composite-routes`) + return data +} + +export async function createCompositeRoute( + id: number, + route: CompositeModelRouteInput +): Promise { + const { data } = await apiClient.post( + `/admin/groups/${id}/composite-routes`, + route + ) + return data +} + +export async function updateCompositeRoute( + id: number, + routeId: number, + route: CompositeModelRouteInput +): Promise { + const { data } = await apiClient.put( + `/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 { + const { data } = await apiClient.post( + `/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, diff --git a/frontend/src/i18n/locales/en/admin/overview.ts b/frontend/src/i18n/locales/en/admin/overview.ts index 98799ac572..7782f55583 100644 --- a/frontend/src/i18n/locales/en/admin/overview.ts +++ b/frontend/src/i18n/locales/en/admin/overview.ts @@ -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.', diff --git a/frontend/src/i18n/locales/zh/admin/overview.ts b/frontend/src/i18n/locales/zh/admin/overview.ts index 5d1a3864e5..39cbe8a264 100644 --- a/frontend/src/i18n/locales/zh/admin/overview.ts +++ b/frontend/src/i18n/locales/zh/admin/overview.ts @@ -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: diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts index 25f826b06c..4f96ffda28 100644 --- a/frontend/src/types/index.ts +++ b/frontend/src/types/index.ts @@ -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 + 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 + 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 | '' + upstream_model: string + endpoint: CompositeRouteEndpoint + route?: CompositeModelRoute + reason?: string +} + export interface ApiKey { id: number user_id: number diff --git a/frontend/src/views/admin/GroupsView.vue b/frontend/src/views/admin/GroupsView.vue index a2c4c7583e..6ebe04285f 100644 --- a/frontend/src/views/admin/GroupsView.vue +++ b/frontend/src/views/admin/GroupsView.vue @@ -386,6 +386,16 @@ }} + + + +
+
+ {{ t("common.loading") }} +
+
+ {{ t("admin.groups.compositeRoutes.empty") }} +
+
+ + + + + + + + + + + + + + + + + +
+ {{ t("admin.groups.compositeRoutes.publicModel") }} + + {{ t("admin.groups.compositeRoutes.target") }} + + {{ t("admin.groups.compositeRoutes.scope") }} + + {{ t("admin.groups.columns.actions") }} +
+
+ {{ route.public_model }} +
+
+ {{ + compositeRouteMatchLabel(route.match_type) + }} + + {{ t("admin.accounts.status.inactive") }} + +
+
+
+ + {{ formatCompositePlatform(route.target_platform) }} +
+
+ {{ route.upstream_model || route.public_model }} +
+
+
+ {{ formatCompositeEndpoint(route.endpoint) }} +
+
+ {{ t("admin.groups.compositeRoutes.priority") }}: + {{ route.priority }} +
+
+
+ + +
+
+
+
+ + +
+
+
+

+ {{ + compositeRouteEditingId + ? t("admin.groups.compositeRoutes.editRoute") + : t("admin.groups.compositeRoutes.addRoute") + }} +

+ +
+ +
+ + +
+ +
+
+ + +
+
+ +
+
+ + +
+
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+ +
+

+ {{ t("admin.groups.compositeRoutes.preview") }} +

+
+ +
+