@canarycoders/ai 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +174 -0
- package/dist/compat.cjs +17 -0
- package/dist/compat.cjs.map +1 -0
- package/dist/compat.d.cts +19 -0
- package/dist/compat.d.ts +19 -0
- package/dist/compat.js +14 -0
- package/dist/compat.js.map +1 -0
- package/dist/index.cjs +1658 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1070 -0
- package/dist/index.d.ts +1068 -0
- package/dist/index.js +1638 -0
- package/dist/index.js.map +1 -0
- package/dist/realtime-BWDkXcj9.d.cts +61 -0
- package/dist/realtime-BWDkXcj9.d.ts +61 -0
- package/dist/realtime-client/index.cjs +82 -0
- package/dist/realtime-client/index.cjs.map +1 -0
- package/dist/realtime-client/index.d.cts +30 -0
- package/dist/realtime-client/index.d.ts +30 -0
- package/dist/realtime-client/index.js +80 -0
- package/dist/realtime-client/index.js.map +1 -0
- package/package.json +81 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// src/realtime-client/index.ts
|
|
2
|
+
async function connectRealtime(options) {
|
|
3
|
+
const { credential } = options;
|
|
4
|
+
if (credential.kind !== "openai-realtime") {
|
|
5
|
+
throw new Error(
|
|
6
|
+
`connectRealtime handles "openai-realtime" credentials; got "${credential.kind}". For ElevenLabs convai, use the ElevenLabs client with credential.url.`
|
|
7
|
+
);
|
|
8
|
+
}
|
|
9
|
+
const pc = new RTCPeerConnection();
|
|
10
|
+
const wantMic = options.microphone ?? true;
|
|
11
|
+
const wantAudio = options.audioOutput ?? true;
|
|
12
|
+
if (wantAudio) {
|
|
13
|
+
const audioEl = new Audio();
|
|
14
|
+
audioEl.autoplay = true;
|
|
15
|
+
pc.addEventListener("track", (event) => {
|
|
16
|
+
const stream = event.streams[0];
|
|
17
|
+
if (stream) audioEl.srcObject = stream;
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
if (wantMic) {
|
|
21
|
+
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
22
|
+
for (const track of stream.getTracks()) pc.addTrack(track, stream);
|
|
23
|
+
}
|
|
24
|
+
const dc = pc.createDataChannel("oai-events");
|
|
25
|
+
dc.addEventListener("open", () => options.onOpen?.());
|
|
26
|
+
dc.addEventListener("close", () => options.onClose?.());
|
|
27
|
+
dc.addEventListener("message", (event) => {
|
|
28
|
+
const data = event.data;
|
|
29
|
+
try {
|
|
30
|
+
options.onEvent?.(typeof data === "string" ? JSON.parse(data) : data);
|
|
31
|
+
} catch {
|
|
32
|
+
options.onEvent?.(data);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
const offer = await pc.createOffer();
|
|
36
|
+
await pc.setLocalDescription(offer);
|
|
37
|
+
let answerSdp;
|
|
38
|
+
try {
|
|
39
|
+
const res = await fetch(credential.url, {
|
|
40
|
+
method: "POST",
|
|
41
|
+
body: offer.sdp ?? "",
|
|
42
|
+
headers: {
|
|
43
|
+
Authorization: `Bearer ${credential.token ?? ""}`,
|
|
44
|
+
"Content-Type": "application/sdp"
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
if (!res.ok) {
|
|
48
|
+
const detail = await res.text().catch(() => "");
|
|
49
|
+
throw new Error(`Realtime SDP exchange failed (${res.status}): ${detail}`);
|
|
50
|
+
}
|
|
51
|
+
answerSdp = await res.text();
|
|
52
|
+
} catch (err) {
|
|
53
|
+
pc.close();
|
|
54
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
55
|
+
options.onError?.(error);
|
|
56
|
+
throw error;
|
|
57
|
+
}
|
|
58
|
+
await pc.setRemoteDescription({ type: "answer", sdp: answerSdp });
|
|
59
|
+
return {
|
|
60
|
+
pc,
|
|
61
|
+
dataChannel: dc,
|
|
62
|
+
send(event) {
|
|
63
|
+
if (dc.readyState === "open") dc.send(JSON.stringify(event));
|
|
64
|
+
},
|
|
65
|
+
close() {
|
|
66
|
+
try {
|
|
67
|
+
dc.close();
|
|
68
|
+
} catch {
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
pc.close();
|
|
72
|
+
} catch {
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export { connectRealtime };
|
|
79
|
+
//# sourceMappingURL=index.js.map
|
|
80
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/realtime-client/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { BrokeredCredential } from \"../types/realtime\";\n\nexport interface RealtimeConnectOptions {\n /** credential minted server-side via `client.realtime.sessions.create()` */\n credential: BrokeredCredential;\n /** capture the microphone and stream it to the model (default true) */\n microphone?: boolean;\n /** play the model's audio output (default true) */\n audioOutput?: boolean;\n onEvent?: (event: unknown) => void;\n onError?: (error: Error) => void;\n onOpen?: () => void;\n onClose?: () => void;\n}\n\nexport interface RealtimeConnection {\n pc: RTCPeerConnection;\n dataChannel: RTCDataChannel;\n /** send a JSON event over the data channel */\n send(event: unknown): void;\n /** tear down the data channel and peer connection */\n close(): void;\n}\n\n/**\n * Open a WebRTC session to OpenAI's realtime endpoint using a credential\n * brokered by the server. Browser-only. For ElevenLabs conversational agents,\n * feed `credential.url` (the signed URL) to the ElevenLabs client instead.\n */\nexport async function connectRealtime(\n options: RealtimeConnectOptions,\n): Promise<RealtimeConnection> {\n const { credential } = options;\n if (credential.kind !== \"openai-realtime\") {\n throw new Error(\n `connectRealtime handles \"openai-realtime\" credentials; got \"${credential.kind}\". For ElevenLabs convai, use the ElevenLabs client with credential.url.`,\n );\n }\n\n const pc = new RTCPeerConnection();\n const wantMic = options.microphone ?? true;\n const wantAudio = options.audioOutput ?? true;\n\n if (wantAudio) {\n const audioEl = new Audio();\n audioEl.autoplay = true;\n pc.addEventListener(\"track\", (event) => {\n const stream = (event as RTCTrackEvent).streams[0];\n if (stream) audioEl.srcObject = stream;\n });\n }\n\n if (wantMic) {\n const stream = await navigator.mediaDevices.getUserMedia({ audio: true });\n for (const track of stream.getTracks()) pc.addTrack(track, stream);\n }\n\n const dc = pc.createDataChannel(\"oai-events\");\n dc.addEventListener(\"open\", () => options.onOpen?.());\n dc.addEventListener(\"close\", () => options.onClose?.());\n dc.addEventListener(\"message\", (event) => {\n const data = (event as MessageEvent).data;\n try {\n options.onEvent?.(typeof data === \"string\" ? JSON.parse(data) : data);\n } catch {\n options.onEvent?.(data);\n }\n });\n\n const offer = await pc.createOffer();\n await pc.setLocalDescription(offer);\n\n let answerSdp: string;\n try {\n const res = await fetch(credential.url, {\n method: \"POST\",\n body: offer.sdp ?? \"\",\n headers: {\n Authorization: `Bearer ${credential.token ?? \"\"}`,\n \"Content-Type\": \"application/sdp\",\n },\n });\n if (!res.ok) {\n const detail = await res.text().catch(() => \"\");\n throw new Error(`Realtime SDP exchange failed (${res.status}): ${detail}`);\n }\n answerSdp = await res.text();\n } catch (err) {\n pc.close();\n const error = err instanceof Error ? err : new Error(String(err));\n options.onError?.(error);\n throw error;\n }\n\n await pc.setRemoteDescription({ type: \"answer\", sdp: answerSdp });\n\n return {\n pc,\n dataChannel: dc,\n send(event: unknown) {\n if (dc.readyState === \"open\") dc.send(JSON.stringify(event));\n },\n close() {\n try {\n dc.close();\n } catch {\n /* noop */\n }\n try {\n pc.close();\n } catch {\n /* noop */\n }\n },\n };\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@canarycoders/ai",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Official TypeScript SDK for the CanaryCoders AI multi-provider gateway",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "CanaryCoders",
|
|
7
|
+
"homepage": "https://github.com/CanaryCoders/canaryllm-sdk#readme",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/CanaryCoders/canaryllm-sdk.git"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/CanaryCoders/canaryllm-sdk/issues"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"canaryllm",
|
|
17
|
+
"llm",
|
|
18
|
+
"openai",
|
|
19
|
+
"anthropic",
|
|
20
|
+
"gemini",
|
|
21
|
+
"sdk",
|
|
22
|
+
"gateway",
|
|
23
|
+
"ai"
|
|
24
|
+
],
|
|
25
|
+
"type": "module",
|
|
26
|
+
"sideEffects": false,
|
|
27
|
+
"main": "./dist/index.cjs",
|
|
28
|
+
"module": "./dist/index.js",
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"files": [
|
|
31
|
+
"dist",
|
|
32
|
+
"README.md",
|
|
33
|
+
"LICENSE"
|
|
34
|
+
],
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "public",
|
|
37
|
+
"registry": "https://registry.npmjs.org/"
|
|
38
|
+
},
|
|
39
|
+
"exports": {
|
|
40
|
+
".": {
|
|
41
|
+
"types": "./dist/index.d.ts",
|
|
42
|
+
"import": "./dist/index.js",
|
|
43
|
+
"require": "./dist/index.cjs"
|
|
44
|
+
},
|
|
45
|
+
"./compat": {
|
|
46
|
+
"types": "./dist/compat.d.ts",
|
|
47
|
+
"import": "./dist/compat.js",
|
|
48
|
+
"require": "./dist/compat.cjs"
|
|
49
|
+
},
|
|
50
|
+
"./realtime-client": {
|
|
51
|
+
"browser": "./dist/realtime-client/index.js",
|
|
52
|
+
"types": "./dist/realtime-client/index.d.ts",
|
|
53
|
+
"import": "./dist/realtime-client/index.js",
|
|
54
|
+
"require": "./dist/realtime-client/index.cjs"
|
|
55
|
+
},
|
|
56
|
+
"./package.json": "./package.json"
|
|
57
|
+
},
|
|
58
|
+
"engines": {
|
|
59
|
+
"node": ">=18",
|
|
60
|
+
"bun": ">=1.0.0"
|
|
61
|
+
},
|
|
62
|
+
"scripts": {
|
|
63
|
+
"build": "tsup",
|
|
64
|
+
"prepare": "tsup",
|
|
65
|
+
"typecheck": "tsc --noEmit",
|
|
66
|
+
"lint": "eslint src",
|
|
67
|
+
"test": "bun test test/unit",
|
|
68
|
+
"test:integration": "bun test test/integration",
|
|
69
|
+
"gen:openapi": "bun run scripts/gen-openapi-types.ts",
|
|
70
|
+
"prepublishOnly": "bun run build && bun run typecheck && bun run test"
|
|
71
|
+
},
|
|
72
|
+
"devDependencies": {
|
|
73
|
+
"@types/bun": "^1.1.14",
|
|
74
|
+
"@types/node": "^22.10.2",
|
|
75
|
+
"eslint": "^9.17.0",
|
|
76
|
+
"openapi-typescript": "^7.4.4",
|
|
77
|
+
"tsup": "^8.3.5",
|
|
78
|
+
"typescript": "^5.7.2",
|
|
79
|
+
"typescript-eslint": "^8.18.1"
|
|
80
|
+
}
|
|
81
|
+
}
|