File size: 3,669 Bytes
76289e7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
94
95
96
97
98
99
100
101
102
103
104
import { asFiniteNumber } from "@openclaw/normalization-core/number-coercion";
import { normalizeNullableString } from "@openclaw/normalization-core/string-coerce";
import type { ContextEngineHostSupport } from "./host-compat.js";
import type {
  ContextEngineRuntimeReasonCode,
  ContextEngineSelectionSource,
  ContextEngineRuntimeMode,
  ContextEngineRuntimeSettings,
} from "./types.js";

type OptionalString = string | null | undefined;

const RUNTIME_REASON_CODES = new Set<ContextEngineRuntimeReasonCode>([
  "provider_timeout",
  "provider_unavailable",
  "rate_limited",
  "context_overflow",
  "runtime_unavailable",
  "unknown",
]);
const RUNTIME_REASON_PATTERNS: Array<[ContextEngineRuntimeReasonCode, RegExp]> = [
  ["provider_timeout", /timeout/iu],
  ["rate_limited", /rate|limit|429/iu],
  ["context_overflow", /overflow|context|pressure/iu],
  ["runtime_unavailable", /runtime/iu],
  ["provider_unavailable", /provider|primary|unavailable/iu],
];

function normalizeReasonCode(value: OptionalString): ContextEngineRuntimeReasonCode | null {
  const normalized = normalizeNullableString(value);
  if (!normalized) {
    return null;
  }
  if (RUNTIME_REASON_CODES.has(normalized as ContextEngineRuntimeReasonCode)) {
    return normalized as ContextEngineRuntimeReasonCode;
  }

  return RUNTIME_REASON_PATTERNS.find(([, pattern]) => pattern.test(normalized))?.[0] ?? "unknown";
}

export function buildContextEngineRuntimeSettings(params: {
  mode?: ContextEngineRuntimeMode;
  harnessId?: OptionalString;
  runtimeId?: OptionalString;
  requestedModel?: OptionalString;
  resolvedModel?: OptionalString;
  provider?: OptionalString;
  modelFamily?: OptionalString;
  selectedContextEngineId?: OptionalString;
  contextEngineSelectionSource?: ContextEngineSelectionSource;
  fallbackReason?: OptionalString;
  degradedReason?: OptionalString;
  promptTokenBudget?: number | null;
  maxOutputTokens?: number | null;
  contextEngineHost: ContextEngineHostSupport;
}): ContextEngineRuntimeSettings {
  const hostId = normalizeNullableString(params.contextEngineHost.id);
  const selectedId = normalizeNullableString(params.selectedContextEngineId);
  const selectionSource =
    params.contextEngineSelectionSource ?? (selectedId ? "configured" : "unknown");

  const requestedModel = normalizeNullableString(params.requestedModel);
  const resolvedModel = normalizeNullableString(params.resolvedModel);
  const fallbackReason = normalizeReasonCode(params.fallbackReason);
  const degradedReason = normalizeReasonCode(params.degradedReason);
  const resolvedViaFallback =
    requestedModel !== null && resolvedModel !== null && requestedModel !== resolvedModel;
  const mode =
    params.mode ??
    (degradedReason ? "degraded" : fallbackReason || resolvedViaFallback ? "fallback" : "normal");

  return {
    schemaVersion: 1,
    runtime: {
      host: "openclaw",
      mode,
      harnessId: normalizeNullableString(params.harnessId),
      runtimeId: normalizeNullableString(params.runtimeId),
    },
    model: {
      requested: requestedModel,
      resolved: resolvedModel,
      provider: normalizeNullableString(params.provider),
      family: normalizeNullableString(params.modelFamily),
    },
    contextEngineSelection: {
      selectedId,
      source: selectionSource,
    },
    executionHost: {
      id: hostId,
      label: normalizeNullableString(params.contextEngineHost.label),
    },
    limits: {
      promptTokenBudget: asFiniteNumber(params.promptTokenBudget) ?? null,
      maxOutputTokens: asFiniteNumber(params.maxOutputTokens) ?? null,
    },
    diagnostics: {
      fallbackReason,
      degradedReason,
    },
  };
}