mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-24 11:25:50 -06:00
dc60a9442d
* feat(talk): add GPT-Live gateway relay peer * fix(talk): ignore duplicate GPT-Live sideband audio * fix(openai): harden GPT-Live relay startup * fix(deps): replace vulnerable werift ip helper * fix(deps): scope werift ip override version * fix(openai): normalize relay startup errors * test(ci): register GPT-Live gateway live shard * fix(talk): tighten GPT-Live relay readiness * fix(talk): keep relay override helper private * fix(talk): resolve relay readiness with launch model * fix(openai): preserve GPT-Live media clock * fix(talk): align GPT-Live relay readiness and framing * fix(openai): expire pending GPT-Live reservations * fix(openai): harden GPT-Live delegation audio * style(openai): satisfy RTP reorder lint
107 lines
3.8 KiB
TypeScript
107 lines
3.8 KiB
TypeScript
// Openai tests cover GPT-Live (quicksilver) realtime voice gating.
|
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
import { isOpenAIGptLiveModel, isSupportedOpenAIGptLiveModel } from "./realtime-quicksilver.js";
|
|
import { buildOpenAIRealtimeVoiceProvider } from "./realtime-voice-provider.js";
|
|
|
|
const mintSecretMock = vi.hoisted(() => vi.fn());
|
|
|
|
vi.mock("./realtime-provider-shared.js", async () => {
|
|
const actual = await vi.importActual<typeof import("./realtime-provider-shared.js")>(
|
|
"./realtime-provider-shared.js",
|
|
);
|
|
return {
|
|
...actual,
|
|
createOpenAIRealtimeClientSecret: mintSecretMock,
|
|
};
|
|
});
|
|
|
|
describe("openai gpt-live model detection", () => {
|
|
it("matches the gpt-live model family", () => {
|
|
expect(isOpenAIGptLiveModel("gpt-live-1")).toBe(true);
|
|
expect(isOpenAIGptLiveModel("gpt-live-1-mini")).toBe(true);
|
|
expect(isOpenAIGptLiveModel(" GPT-Live-1 ")).toBe(true);
|
|
expect(isOpenAIGptLiveModel("gpt-live")).toBe(true);
|
|
});
|
|
|
|
it("rejects non-live models and prefix lookalikes", () => {
|
|
expect(isOpenAIGptLiveModel(undefined)).toBe(false);
|
|
expect(isOpenAIGptLiveModel("gpt-realtime-2.1")).toBe(false);
|
|
expect(isOpenAIGptLiveModel("gpt-liveish")).toBe(false);
|
|
});
|
|
|
|
it("advertises only curated /v1/live models", () => {
|
|
expect(isSupportedOpenAIGptLiveModel("gpt-live-1-codex")).toBe(true);
|
|
expect(isSupportedOpenAIGptLiveModel(" GPT-Live-1-Boulder-Alpha ")).toBe(true);
|
|
expect(isSupportedOpenAIGptLiveModel("gpt-live-1")).toBe(false);
|
|
expect(isSupportedOpenAIGptLiveModel("gpt-live-1-mini")).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe("openai realtime voice provider gpt-live transport routing", () => {
|
|
beforeEach(() => {
|
|
mintSecretMock.mockReset();
|
|
mintSecretMock.mockResolvedValue({ value: "ek_test", expiresAt: 1234 });
|
|
});
|
|
|
|
it("keeps GA realtime browser sessions working", async () => {
|
|
const provider = buildOpenAIRealtimeVoiceProvider();
|
|
const session = await provider.createBrowserSession?.({
|
|
providerConfig: { apiKey: "test-key" },
|
|
model: "gpt-realtime-2.1",
|
|
instructions: "Keep GA behavior unchanged.",
|
|
});
|
|
expect(session).toMatchObject({
|
|
transport: "webrtc",
|
|
offerUrl: "https://api.openai.com/v1/realtime/calls",
|
|
});
|
|
expect(mintSecretMock.mock.calls[0]?.[0]?.session).toMatchObject({
|
|
type: "realtime",
|
|
model: "gpt-realtime-2.1",
|
|
instructions: "Keep GA behavior unchanged.",
|
|
});
|
|
});
|
|
|
|
it("routes gpt-live by the host-owned delegation seam", () => {
|
|
const provider = buildOpenAIRealtimeVoiceProvider();
|
|
const callbacks = {
|
|
onAudio: vi.fn(),
|
|
onClearAudio: vi.fn(),
|
|
};
|
|
expect(
|
|
provider.createBridge({
|
|
...callbacks,
|
|
providerConfig: { apiKey: "test-key", model: "gpt-live-1-codex" },
|
|
}),
|
|
).toMatchObject({ supportsToolResultContinuation: true });
|
|
expect(
|
|
provider.createBridge({
|
|
...callbacks,
|
|
providerConfig: { apiKey: "test-key", model: "gpt-live-1" },
|
|
runAgentConsult: vi.fn(async () => ({ text: "done" })),
|
|
}),
|
|
).toMatchObject({ supportsToolResultContinuation: false });
|
|
expect(() =>
|
|
provider.createBridge({
|
|
...callbacks,
|
|
providerConfig: { apiKey: "test-key", model: "gpt-realtime-2.1" },
|
|
}),
|
|
).not.toThrow();
|
|
});
|
|
|
|
it("rejects Azure credentials before creating a Platform GPT-Live bridge", () => {
|
|
const provider = buildOpenAIRealtimeVoiceProvider();
|
|
expect(() =>
|
|
provider.createBridge({
|
|
providerConfig: {
|
|
apiKey: "azure-test-key",
|
|
model: "gpt-live-1-codex",
|
|
azureEndpoint: "https://example.openai.azure.com",
|
|
azureDeployment: "realtime",
|
|
},
|
|
onAudio: vi.fn(),
|
|
onClearAudio: vi.fn(),
|
|
}),
|
|
).toThrow("GPT-Live backend WebSocket sessions do not support Azure");
|
|
});
|
|
});
|