import { ServerSettings, type ServerSettingsPatch } from "@t3tools/contracts";import { Schema } from "effect";import { deepMerge } from "./Struct.ts";import { fromLenientJson } from "./schemaJson.ts"; const ServerSettingsJson = fromLenientJson(ServerSettings); export interface PersistedServerObservabilitySettings { readonly otlpTracesUrl: string | undefined; readonly otlpMetricsUrl: string | undefined;} export function normalizePersistedServerSettingString( value: string | null | undefined,): string | undefined { const trimmed = value?.trim(); return trimmed && trimmed.length > 0 ? trimmed : undefined;} export function extractPersistedServerObservabilitySettings(input: { readonly observability?: { readonly otlpTracesUrl?: string; readonly otlpMetricsUrl?: string; };}): PersistedServerObservabilitySettings { return { otlpTracesUrl: normalizePersistedServerSettingString(input.observability?.otlpTracesUrl), otlpMetricsUrl: normalizePersistedServerSettingString(input.observability?.otlpMetricsUrl), };} export function parsePersistedServerObservabilitySettings( raw: string,): PersistedServerObservabilitySettings { try { const decoded = Schema.decodeUnknownSync(ServerSettingsJson)(raw); return extractPersistedServerObservabilitySettings(decoded); } catch { return { otlpTracesUrl: undefined, otlpMetricsUrl: undefined }; }} function shouldReplaceTextGenerationModelSelection( patch: ServerSettingsPatch["textGenerationModelSelection"] | undefined,): boolean { return Boolean(patch && (patch.provider !== undefined || patch.model !== undefined));} /** * Applies a server settings patch while treating textGenerationModelSelection as * replace-on-provider/model updates. This prevents stale nested options from * surviving a reset patch that intentionally omits options. */export function applyServerSettingsPatch( current: ServerSettings, patch: ServerSettingsPatch,): ServerSettings { const selectionPatch = patch.textGenerationModelSelection; const next = deepMerge(current, patch); if (!selectionPatch || !shouldReplaceTextGenerationModelSelection(selectionPatch)) { return next; } return { ...next, textGenerationModelSelection: { provider: selectionPatch.provider ?? current.textGenerationModelSelection.provider, model: selectionPatch.model ?? current.textGenerationModelSelection.model, ...(selectionPatch.options ? { options: selectionPatch.options } : {}), }, };}
import { ServerSettings, type ServerSettingsPatch } from "@t3tools/contracts";
import { Schema } from "effect";
import { deepMerge } from "./Struct.ts";
import { fromLenientJson } from "./schemaJson.ts";
const ServerSettingsJson = fromLenientJson(ServerSettings);
export interface PersistedServerObservabilitySettings {
readonly otlpTracesUrl: string | undefined;
readonly otlpMetricsUrl: string | undefined;
}
export function normalizePersistedServerSettingString(
value: string | null | undefined,
): string | undefined {
const trimmed = value?.trim();
return trimmed && trimmed.length > 0 ? trimmed : undefined;
}
export function extractPersistedServerObservabilitySettings(input: {
readonly observability?: {
readonly otlpTracesUrl?: string;
readonly otlpMetricsUrl?: string;
};
}): PersistedServerObservabilitySettings {
return {
otlpTracesUrl: normalizePersistedServerSettingString(input.observability?.otlpTracesUrl),
otlpMetricsUrl: normalizePersistedServerSettingString(input.observability?.otlpMetricsUrl),
};
}
export function parsePersistedServerObservabilitySettings(
raw: string,
): PersistedServerObservabilitySettings {
try {
const decoded = Schema.decodeUnknownSync(ServerSettingsJson)(raw);
return extractPersistedServerObservabilitySettings(decoded);
} catch {
return { otlpTracesUrl: undefined, otlpMetricsUrl: undefined };
}
}
function shouldReplaceTextGenerationModelSelection(
patch: ServerSettingsPatch["textGenerationModelSelection"] | undefined,
): boolean {
return Boolean(patch && (patch.provider !== undefined || patch.model !== undefined));
}
/**
* Applies a server settings patch while treating textGenerationModelSelection as
* replace-on-provider/model updates. This prevents stale nested options from
* surviving a reset patch that intentionally omits options.
*/
export function applyServerSettingsPatch(
current: ServerSettings,
patch: ServerSettingsPatch,
): ServerSettings {
const selectionPatch = patch.textGenerationModelSelection;
const next = deepMerge(current, patch);
if (!selectionPatch || !shouldReplaceTextGenerationModelSelection(selectionPatch)) {
return next;
}
return {
...next,
textGenerationModelSelection: {
provider: selectionPatch.provider ?? current.textGenerationModelSelection.provider,
model: selectionPatch.model ?? current.textGenerationModelSelection.model,
...(selectionPatch.options ? { options: selectionPatch.options } : {}),
},
};
}