@opengeni/core 1.1.0 → 1.2.1-canary.37221563227001

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  import { type ConfiguredModel, type Settings } from "@opengeni/config";
2
- import { type DefaultModelSelection, type ReasoningEffort, type ScheduledTask, type WorkspaceSessionDefaults, type XaiProviderAccountAuthoritySnapshotV1, type ClaudeProviderAccountAuthoritySnapshotV1 } from "@opengeni/contracts";
2
+ import { type DefaultModelSelection, type ReasoningEffort, type ScheduledTask, type WorkspaceSessionDefaults, type XaiProviderAccountAuthoritySnapshotV1, type ClaudeProviderAccountAuthoritySnapshotV1, type BillingBalance } from "@opengeni/contracts";
3
3
  import { type Database } from "@opengeni/db";
4
4
  import { type WorkspaceModelSelection, type WorkspaceModelSelectionInput } from "./model-catalog.js";
5
5
  /** The model's own default effort, matching the web picker's choice. */
@@ -18,6 +18,7 @@ export type DefaultSessionModelInput = {
18
18
  * `organizationHoldsCredits`).
19
19
  */
20
20
  creditsAvailable: boolean;
21
+ creditBalance?: BillingBalance;
21
22
  };
22
23
  /**
23
24
  * Default model policy for a new chat or scheduled task that names no model.
@@ -36,8 +37,8 @@ export type DefaultSessionModelInput = {
36
37
  * configured credits default (`OPENGENI_CREDITS_DEFAULT_MODEL`, effort
37
38
  * clamped to what the model supports), or the first selectable
38
39
  * credits-billed model when that one is not selectable. When the
39
- * deployment default is already a selectable credits-billed model it is
40
- * never replaced: it keeps the deployment effort, except that it takes the
40
+ * deployment default is already a funded, selectable credits-billed model it
41
+ * keeps the deployment effort, except that it takes the
41
42
  * credits default effort when it is the credits default model itself.
42
43
  * 4. `deployment`: the deployment default with the deployment reasoning effort
43
44
  * when stably admissible; otherwise the first stably admissible catalog
@@ -57,6 +58,7 @@ export declare function creditsDefaultSessionModel(input: {
57
58
  settings: Settings;
58
59
  selections: readonly WorkspaceModelSelection[];
59
60
  workspaceSettings: unknown;
61
+ creditBalance?: BillingBalance;
60
62
  }): DefaultModelSelection | null;
61
63
  /**
62
64
  * Resolve the default for an already-loaded workspace selection. The credit
@@ -86,6 +86,14 @@ export declare function resolveGoalModelAdmission(db: Database, settings: Settin
86
86
  productAccessMode: "configured" | "local" | "managed";
87
87
  billingMode: "disabled" | "stripe";
88
88
  verifiedSignupTrialCreditsEnabled: boolean;
89
+ creditPromotionPolicy: {
90
+ defaultModelIds?: string[] | undefined;
91
+ signupModelIds?: string[] | undefined;
92
+ offers: Record<string, {
93
+ label: string;
94
+ eligibleModelIds?: string[] | undefined;
95
+ }>;
96
+ };
89
97
  entitlementsMode: "managed" | "none" | "static";
90
98
  usageLimitsMode: "managed" | "none" | "static";
91
99
  usageAllowancesEnabled: boolean;
package/dist/index.js CHANGED
@@ -4826,7 +4826,7 @@ function withDeveloperSetupCredentialRestriction(policy, source) {
4826
4826
  import {
4827
4827
  CLAUDE_CONNECTION_KINDS as CLAUDE_CONNECTION_KINDS3,
4828
4828
  claudeProviderId as claudeProviderId2,
4829
- canonicalizeConfiguredModelId as canonicalizeConfiguredModelId2,
4829
+ canonicalizeConfiguredModelId as canonicalizeConfiguredModelId3,
4830
4830
  configuredAllowedModels,
4831
4831
  withCodexCatalogProvider as withCodexCatalogProvider3,
4832
4832
  ORGANIZATION_GATEWAY_MODEL_ID_PREFIX as ORGANIZATION_GATEWAY_MODEL_ID_PREFIX2,
@@ -5088,6 +5088,7 @@ function prepareExternalLinkTaskAdmission(authorization, actor) {
5088
5088
  // src/billing/limits.ts
5089
5089
  import {
5090
5090
  configuredStaticUsageLimits,
5091
+ canonicalizeConfiguredModelId,
5091
5092
  resolveModelProviderForTurn
5092
5093
  } from "@opengeni/config";
5093
5094
  import {
@@ -5096,7 +5097,7 @@ import {
5096
5097
  countActiveOrganizationApiKeysForAccount,
5097
5098
  countScheduledTasksForWorkspace,
5098
5099
  countWorkspacesForAccount,
5099
- getBillingBalance,
5100
+ getSpendableCreditBalance,
5100
5101
  isCodexBilledTurn,
5101
5102
  recordUsageEvent,
5102
5103
  sumUsageQuantity
@@ -5177,11 +5178,19 @@ async function checkCreditBalance(deps, input, externallyBilled) {
5177
5178
  if (!usesCreditLimits(deps) || !isCostlyAction(input.action)) {
5178
5179
  return { allowed: true };
5179
5180
  }
5180
- const balance = await getBillingBalance(deps.db, input.accountId);
5181
+ const balance = await getSpendableCreditBalance(
5182
+ deps.db,
5183
+ input.accountId,
5184
+ input.model ? canonicalizeConfiguredModelId(deps.settings, input.model) : void 0
5185
+ );
5181
5186
  if (balance.balanceMicros > 0) {
5182
5187
  return { allowed: true };
5183
5188
  }
5184
- return { allowed: false, code: "insufficient_credits", message: "insufficient Opengeni credits" };
5189
+ return {
5190
+ allowed: false,
5191
+ code: "insufficient_credits",
5192
+ message: input.model ? "No credits available for this model. Choose a model covered by your promotional credits or add credits." : "No general credits available. Promotional credits cover eligible models only."
5193
+ };
5185
5194
  }
5186
5195
  async function checkStaticCaps(deps, input, funding) {
5187
5196
  const limits = configuredStaticUsageLimits(deps.settings);
@@ -7213,7 +7222,7 @@ function resolveWorkspaceModelSelection(input) {
7213
7222
 
7214
7223
  // src/default-session-model.ts
7215
7224
  import {
7216
- canonicalizeConfiguredModelId,
7225
+ canonicalizeConfiguredModelId as canonicalizeConfiguredModelId2,
7217
7226
  CLAUDE_CONNECTION_KINDS as CLAUDE_CONNECTION_KINDS2
7218
7227
  } from "@opengeni/config";
7219
7228
  import {
@@ -7235,7 +7244,8 @@ import {
7235
7244
  workspaceCodexSubscriptionActive,
7236
7245
  workspaceOpenRouterConnectionActive,
7237
7246
  workspaceVercelAiGatewayConnectionActive,
7238
- organizationHoldsCredits,
7247
+ getBillingBalance,
7248
+ spendableCreditMicros,
7239
7249
  workspaceXaiSubscriptionActive,
7240
7250
  workspaceXaiSubscriptionActiveForAuthority,
7241
7251
  XaiAuthorityPoolInactiveError,
@@ -7371,9 +7381,12 @@ function findSelection(selections, modelId) {
7371
7381
  return selections.find((selection) => selection.model.id === modelId) ?? selections.find((selection) => selection.model.aliases.includes(modelId));
7372
7382
  }
7373
7383
  function creditsCandidate(input) {
7384
+ const selections = input.creditBalance ? input.selections.filter(
7385
+ (selection) => spendableCreditMicros(input.creditBalance, selection.model.id) > 0
7386
+ ) : input.selections;
7374
7387
  const fallbackEffort = input.settings.openaiReasoningEffort;
7375
- const deployment = findSelection(input.selections, input.settings.openaiModel);
7376
- const configured = findSelection(input.selections, input.settings.creditsDefaultModel);
7388
+ const deployment = findSelection(selections, input.settings.openaiModel);
7389
+ const configured = findSelection(selections, input.settings.creditsDefaultModel);
7377
7390
  if (deployment?.availability.selectable && deployment.model.cost === "credits") {
7378
7391
  if (configured?.model.id !== deployment.model.id) return null;
7379
7392
  return {
@@ -7397,7 +7410,7 @@ function creditsCandidate(input) {
7397
7410
  source: "credits"
7398
7411
  };
7399
7412
  }
7400
- const first = input.selections.find(
7413
+ const first = selections.find(
7401
7414
  (selection) => selection.availability.selectable && selection.model.cost === "credits"
7402
7415
  );
7403
7416
  return first ? {
@@ -7446,7 +7459,7 @@ function selectDefaultSessionModel(input) {
7446
7459
  reasoningEffort: defaultReasoningEffortForConfiguredModel(fallback.model, fallbackEffort),
7447
7460
  source: "deployment"
7448
7461
  } : {
7449
- model: canonicalizeConfiguredModelId(input.settings, input.settings.openaiModel),
7462
+ model: canonicalizeConfiguredModelId2(input.settings, input.settings.openaiModel),
7450
7463
  reasoningEffort: fallbackEffort,
7451
7464
  source: "deployment"
7452
7465
  };
@@ -7457,7 +7470,8 @@ function creditsDefaultSessionModel(input) {
7457
7470
  settings: input.settings,
7458
7471
  selections: input.selections,
7459
7472
  workspaceDefaults: resolveWorkspaceSessionDefaults(input.workspaceSettings),
7460
- creditsAvailable: true
7473
+ creditsAvailable: true,
7474
+ ...input.creditBalance ? { creditBalance: input.creditBalance } : {}
7461
7475
  });
7462
7476
  }
7463
7477
  async function resolveDefaultSessionModelForSelections(db, input) {
@@ -7467,10 +7481,13 @@ async function resolveDefaultSessionModelForSelections(db, input) {
7467
7481
  workspaceDefaults: resolveWorkspaceSessionDefaults(input.workspaceSettings)
7468
7482
  };
7469
7483
  const withoutCredits = selectDefaultSessionModel({ ...decision, creditsAvailable: false });
7470
- if (withoutCredits.source !== "deployment" || input.settings.billingMode !== "stripe" || creditsCandidate(decision) === null) {
7484
+ if (withoutCredits.source !== "deployment" || input.settings.billingMode !== "stripe" || !input.selections.some(
7485
+ (selection) => selection.availability.selectable && selection.model.cost === "credits"
7486
+ )) {
7471
7487
  return withoutCredits;
7472
7488
  }
7473
- return await organizationHoldsCredits(db, input.accountId) ? selectDefaultSessionModel({ ...decision, creditsAvailable: true }) : withoutCredits;
7489
+ const creditBalance = await getBillingBalance(db, input.accountId);
7490
+ return selectDefaultSessionModel({ ...decision, creditsAvailable: true, creditBalance });
7474
7491
  }
7475
7492
  async function connectionRestrictionsAndXaiReadiness(db, settings, context) {
7476
7493
  const { workspaceId, subjectId, xaiAuthoritySnapshot } = context;
@@ -9636,7 +9653,7 @@ function canonicalConfiguredModel(settings, model) {
9636
9653
  if (model === null || model === void 0) {
9637
9654
  return model;
9638
9655
  }
9639
- const canonicalModel = canonicalizeConfiguredModelId2(settings, model);
9656
+ const canonicalModel = canonicalizeConfiguredModelId3(settings, model);
9640
9657
  if (canonicalModel.startsWith(CODEX_MODEL_ID_PREFIX) && settings.resolvedCodexModelsJson !== void 0) {
9641
9658
  if (settings.codexSubscriptionEnabled && configuredAllowedModels(withCodexCatalogProvider3(settings)).includes(canonicalModel)) {
9642
9659
  return canonicalModel;
@@ -12397,7 +12414,7 @@ import {
12397
12414
  applyCreditDebitAfterUse,
12398
12415
  checkWorkspaceAllowance as checkWorkspaceAllowance2,
12399
12416
  creditDebitAttributionForTurn,
12400
- getBillingBalance as getBillingBalance2,
12417
+ getSpendableCreditBalance as getSpendableCreditBalance2,
12401
12418
  listKnowledgeEntries,
12402
12419
  recordUsageEvent as recordUsageEvent2,
12403
12420
  sumUsageQuantity as sumUsageQuantity2,
@@ -12527,7 +12544,7 @@ async function searchKnowledgeEntries(db, context, input, embedder, settings) {
12527
12544
  context.accountId,
12528
12545
  context.workspaceId,
12529
12546
  async (lockedDb) => {
12530
- const balance = await getBillingBalance2(lockedDb, context.accountId);
12547
+ const balance = await getSpendableCreditBalance2(lockedDb, context.accountId);
12531
12548
  if (balance.balanceMicros <= 0)
12532
12549
  return keywordFallback(lockedDb, new KnowledgeVectorFundingError(), "awaiting_funding");
12533
12550
  const attribution = billingActor.kind === "agent" ? await creditDebitAttributionForTurn(lockedDb, {
@@ -12773,10 +12790,13 @@ import { isCodexBilledModel as isCodexBilledModel2 } from "@opengeni/codex";
12773
12790
  import { getWorkspaceModelPolicy as getWorkspaceModelPolicy3 } from "@opengeni/db";
12774
12791
 
12775
12792
  // src/billing/agent-run-admission.ts
12776
- import { configuredStaticUsageLimits as configuredStaticUsageLimits2 } from "@opengeni/config";
12793
+ import {
12794
+ canonicalizeConfiguredModelId as canonicalizeConfiguredModelId4,
12795
+ configuredStaticUsageLimits as configuredStaticUsageLimits2
12796
+ } from "@opengeni/config";
12777
12797
  import {
12778
12798
  checkWorkspaceAllowance as checkWorkspaceAllowance3,
12779
- getBillingBalance as getBillingBalance3,
12799
+ getSpendableCreditBalance as getSpendableCreditBalance3,
12780
12800
  isCodexBilledTurn as isCodexBilledTurn2,
12781
12801
  sumUsageQuantity as sumUsageQuantity3
12782
12802
  } from "@opengeni/db";
@@ -12802,7 +12822,11 @@ async function agentRunAdmissionDenial(services, input) {
12802
12822
  });
12803
12823
  if (!decision.allowed) return "insufficient_credits";
12804
12824
  } else {
12805
- const balance = await getBillingBalance3(services.db, input.accountId);
12825
+ const balance = await getSpendableCreditBalance3(
12826
+ services.db,
12827
+ input.accountId,
12828
+ canonicalizeConfiguredModelId4(services.settings, input.model)
12829
+ );
12806
12830
  if (balance.balanceMicros <= 0) return "insufficient_credits";
12807
12831
  }
12808
12832
  }
@@ -20391,7 +20415,7 @@ import {
20391
20415
  withWorkspaceSubjectRls as withWorkspaceSubjectRls6
20392
20416
  } from "@opengeni/db";
20393
20417
  import { HTTPException as HTTPException29 } from "hono/http-exception";
20394
- import { canonicalizeConfiguredModelId as canonicalizeConfiguredModelId3 } from "@opengeni/config";
20418
+ import { canonicalizeConfiguredModelId as canonicalizeConfiguredModelId5 } from "@opengeni/config";
20395
20419
  function hasOwn(value, key) {
20396
20420
  return typeof value === "object" && value !== null && Object.hasOwn(value, key);
20397
20421
  }
@@ -20416,7 +20440,7 @@ function mapNewSessionDraft(row) {
20416
20440
  }
20417
20441
  function draftModelProvided(settings, draft) {
20418
20442
  if (draft.modelProvided !== void 0) return draft.modelProvided;
20419
- return !(canonicalizeConfiguredModelId3(settings, draft.model) === canonicalizeConfiguredModelId3(settings, settings.openaiModel) && draft.reasoningEffort === settings.openaiReasoningEffort && draft.latencyMode === "standard");
20443
+ return !(canonicalizeConfiguredModelId5(settings, draft.model) === canonicalizeConfiguredModelId5(settings, settings.openaiModel) && draft.reasoningEffort === settings.openaiReasoningEffort && draft.latencyMode === "standard");
20420
20444
  }
20421
20445
  async function actorDefaultModel(deps, grant, workspaceId) {
20422
20446
  return await resolveDefaultSessionModel(deps.db, deps.settings, {