mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-12 09:41:11 +00:00
refactor: dedupe openai realtime provider helpers
This commit is contained in:
33
extensions/openai/realtime-provider-shared.ts
Normal file
33
extensions/openai/realtime-provider-shared.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
export function trimToUndefined(value: unknown): string | undefined {
|
||||
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
||||
}
|
||||
|
||||
export function asFiniteNumber(value: unknown): number | undefined {
|
||||
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
||||
}
|
||||
|
||||
export function asObjectRecord(value: unknown): Record<string, unknown> | undefined {
|
||||
return typeof value === "object" && value !== null && !Array.isArray(value)
|
||||
? (value as Record<string, unknown>)
|
||||
: undefined;
|
||||
}
|
||||
|
||||
export function readRealtimeErrorDetail(error: unknown): string {
|
||||
if (typeof error === "string" && error) {
|
||||
return error;
|
||||
}
|
||||
const message = asObjectRecord(error)?.message;
|
||||
if (typeof message === "string" && message) {
|
||||
return message;
|
||||
}
|
||||
return "Unknown error";
|
||||
}
|
||||
|
||||
export function resolveOpenAIProviderConfigRecord(
|
||||
config: Record<string, unknown>,
|
||||
): Record<string, unknown> | undefined {
|
||||
const providers = asObjectRecord(config.providers);
|
||||
return (
|
||||
asObjectRecord(providers?.openai) ?? asObjectRecord(config.openai) ?? asObjectRecord(config)
|
||||
);
|
||||
}
|
||||
@@ -6,6 +6,12 @@ import type {
|
||||
} from "openclaw/plugin-sdk/realtime-transcription";
|
||||
import { normalizeResolvedSecretInputString } from "openclaw/plugin-sdk/secret-input";
|
||||
import WebSocket from "ws";
|
||||
import {
|
||||
asFiniteNumber,
|
||||
readRealtimeErrorDetail,
|
||||
resolveOpenAIProviderConfigRecord,
|
||||
trimToUndefined,
|
||||
} from "./realtime-provider-shared.js";
|
||||
|
||||
type OpenAIRealtimeTranscriptionProviderConfig = {
|
||||
apiKey?: string;
|
||||
@@ -28,36 +34,10 @@ type RealtimeEvent = {
|
||||
error?: unknown;
|
||||
};
|
||||
|
||||
function trimToUndefined(value: unknown): string | undefined {
|
||||
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
||||
}
|
||||
|
||||
function asNumber(value: unknown): number | undefined {
|
||||
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
||||
}
|
||||
|
||||
function asObject(value: unknown): Record<string, unknown> | undefined {
|
||||
return typeof value === "object" && value !== null && !Array.isArray(value)
|
||||
? (value as Record<string, unknown>)
|
||||
: undefined;
|
||||
}
|
||||
|
||||
function readRealtimeErrorDetail(error: unknown): string {
|
||||
if (typeof error === "string" && error) {
|
||||
return error;
|
||||
}
|
||||
const message = asObject(error)?.message;
|
||||
if (typeof message === "string" && message) {
|
||||
return message;
|
||||
}
|
||||
return "Unknown error";
|
||||
}
|
||||
|
||||
function normalizeProviderConfig(
|
||||
config: RealtimeTranscriptionProviderConfig,
|
||||
): OpenAIRealtimeTranscriptionProviderConfig {
|
||||
const providers = asObject(config.providers);
|
||||
const raw = asObject(providers?.openai) ?? asObject(config.openai) ?? asObject(config);
|
||||
const raw = resolveOpenAIProviderConfigRecord(config);
|
||||
return {
|
||||
apiKey:
|
||||
normalizeResolvedSecretInputString({
|
||||
@@ -69,8 +49,8 @@ function normalizeProviderConfig(
|
||||
path: "plugins.entries.voice-call.config.streaming.openaiApiKey",
|
||||
}),
|
||||
model: trimToUndefined(raw?.model) ?? trimToUndefined(raw?.sttModel),
|
||||
silenceDurationMs: asNumber(raw?.silenceDurationMs),
|
||||
vadThreshold: asNumber(raw?.vadThreshold),
|
||||
silenceDurationMs: asFiniteNumber(raw?.silenceDurationMs),
|
||||
vadThreshold: asFiniteNumber(raw?.vadThreshold),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,12 @@ import type {
|
||||
} from "openclaw/plugin-sdk/realtime-voice";
|
||||
import { normalizeResolvedSecretInputString } from "openclaw/plugin-sdk/secret-input";
|
||||
import WebSocket from "ws";
|
||||
import {
|
||||
asFiniteNumber,
|
||||
readRealtimeErrorDetail,
|
||||
resolveOpenAIProviderConfigRecord,
|
||||
trimToUndefined,
|
||||
} from "./realtime-provider-shared.js";
|
||||
|
||||
export type OpenAIRealtimeVoice =
|
||||
| "alloy"
|
||||
@@ -78,36 +84,10 @@ type RealtimeSessionUpdate = {
|
||||
};
|
||||
};
|
||||
|
||||
function trimToUndefined(value: unknown): string | undefined {
|
||||
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
||||
}
|
||||
|
||||
function asNumber(value: unknown): number | undefined {
|
||||
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
||||
}
|
||||
|
||||
function asObject(value: unknown): Record<string, unknown> | undefined {
|
||||
return typeof value === "object" && value !== null && !Array.isArray(value)
|
||||
? (value as Record<string, unknown>)
|
||||
: undefined;
|
||||
}
|
||||
|
||||
function readRealtimeErrorDetail(error: unknown): string {
|
||||
if (typeof error === "string" && error) {
|
||||
return error;
|
||||
}
|
||||
const message = asObject(error)?.message;
|
||||
if (typeof message === "string" && message) {
|
||||
return message;
|
||||
}
|
||||
return "Unknown error";
|
||||
}
|
||||
|
||||
function normalizeProviderConfig(
|
||||
config: RealtimeVoiceProviderConfig,
|
||||
): OpenAIRealtimeVoiceProviderConfig {
|
||||
const providers = asObject(config.providers);
|
||||
const raw = asObject(providers?.openai) ?? asObject(config.openai) ?? asObject(config);
|
||||
const raw = resolveOpenAIProviderConfigRecord(config);
|
||||
return {
|
||||
apiKey: normalizeResolvedSecretInputString({
|
||||
value: raw?.apiKey,
|
||||
@@ -115,10 +95,10 @@ function normalizeProviderConfig(
|
||||
}),
|
||||
model: trimToUndefined(raw?.model),
|
||||
voice: trimToUndefined(raw?.voice) as OpenAIRealtimeVoice | undefined,
|
||||
temperature: asNumber(raw?.temperature),
|
||||
vadThreshold: asNumber(raw?.vadThreshold),
|
||||
silenceDurationMs: asNumber(raw?.silenceDurationMs),
|
||||
prefixPaddingMs: asNumber(raw?.prefixPaddingMs),
|
||||
temperature: asFiniteNumber(raw?.temperature),
|
||||
vadThreshold: asFiniteNumber(raw?.vadThreshold),
|
||||
silenceDurationMs: asFiniteNumber(raw?.silenceDurationMs),
|
||||
prefixPaddingMs: asFiniteNumber(raw?.prefixPaddingMs),
|
||||
azureEndpoint: trimToUndefined(raw?.azureEndpoint),
|
||||
azureDeployment: trimToUndefined(raw?.azureDeployment),
|
||||
azureApiVersion: trimToUndefined(raw?.azureApiVersion),
|
||||
|
||||
Reference in New Issue
Block a user