File size: 3,704 Bytes
cd8bd0a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
/**
 * combos/autoPromote.ts — pure reorder helper for the "auto-promote successful
 * combo model" feature.
 *
 * When the `comboAutoPromoteEnabled` setting is on and a combo model responds
 * successfully, the winning model is moved to position #1 of the persisted
 * combo so future requests try it first.
 *
 * OmniRoute stores `combo.models` as an array of `ComboStep` objects
 * (`{ kind: "model", model, ... }`), unlike the upstream project which stores
 * plain model strings. This helper accepts both shapes and reorders in place
 * without mutating the input, returning `null` when no change is required
 * (winner already first, winner absent, or empty list).
 */

type StepLike = { kind?: unknown; model?: unknown } | string;

/** Extract the model id from a step entry (object or bare string). */
export function comboStepModelId(step: unknown): string | null {
  if (typeof step === "string") return step.trim().length > 0 ? step : null;
  if (step && typeof step === "object") {
    const model = (step as { model?: unknown }).model;
    if (typeof model === "string" && model.trim().length > 0) return model;
  }
  return null;
}

/**
 * Return a reordered copy of `models` with the entry matching `winningModel`
 * moved to the front, or `null` if no reordering is needed/possible.
 *
 * Pure: never mutates the input array or its entries.
 */
export function promoteModelToFront<T extends StepLike>(
  models: readonly T[] | null | undefined,
  winningModel: string | null | undefined
): T[] | null {
  if (!Array.isArray(models) || models.length === 0) return null;
  if (typeof winningModel !== "string" || winningModel.length === 0) return null;

  const matchIndex = models.findIndex((step) => comboStepModelId(step) === winningModel);
  // Winner not in the combo (e.g. global fallback) or already first → no-op.
  if (matchIndex <= 0) return null;

  const winner = models[matchIndex];
  const rest = models.filter((_, index) => index !== matchIndex);
  return [winner, ...rest];
}

interface PromoteComboDeps {
  updateCombo: (id: string, data: { models: unknown[] }) => Promise<unknown>;
  info?: (tag: string, msg: string) => void;
  warn?: (tag: string, msg: string) => void;
}

/**
 * Persist the auto-promotion of a successful combo model to position #1.
 *
 * Opt-in via the `comboAutoPromoteEnabled` setting. Best-effort: a DB failure is
 * logged and swallowed so it never affects the already-successful response.
 * No-op when the flag is off, the combo has no id, or the model is already first
 * / absent. `updateCombo` is injected so this stays unit-testable without a DB.
 */
export async function promoteSuccessfulComboModel(
  combo: { id?: unknown; name?: unknown; models?: unknown } | null | undefined,
  winningModel: string | null | undefined,
  settings: Record<string, unknown> | null | undefined,
  deps: PromoteComboDeps
): Promise<boolean> {
  if (!combo || !settings || !settings.comboAutoPromoteEnabled) return false;
  const comboId = typeof combo.id === "string" ? combo.id : null;
  if (!comboId) return false;
  const reordered = promoteModelToFront(
    Array.isArray(combo.models) ? (combo.models as unknown[]) : null,
    winningModel
  );
  if (!reordered) return false;
  const label = typeof combo.name === "string" ? combo.name : comboId;
  try {
    await deps.updateCombo(comboId, { models: reordered });
    deps.info?.("COMBO", `Model "${winningModel}" succeeded — promoted to #1 in combo "${label}"`);
    return true;
  } catch (dbErr: any) {
    deps.warn?.(
      "COMBO",
      `Failed to promote model "${winningModel}" in combo "${label}": ${
        dbErr?.message || "unknown error"
      }`
    );
    return false;
  }
}