dsh-realtime 0.0.0-stage → 0.1.1
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 +68 -2
- package/lib/error.d.ts +48 -0
- package/lib/error.d.ts.map +1 -0
- package/lib/error.js +56 -0
- package/lib/error.js.map +1 -0
- package/lib/index.d.ts +162 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +239 -0
- package/lib/index.js.map +1 -0
- package/lib/types.d.ts +178 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/types.js +16 -0
- package/lib/types.js.map +1 -0
- package/package.json +59 -3
- package/src/error.ts +61 -0
- package/src/index.ts +320 -0
- package/src/types.ts +196 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Travis Driessen
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,69 @@
|
|
|
1
|
-
#
|
|
1
|
+
# dsh-realtime
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**A realtime voice capability seam for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness).**
|
|
4
|
+
|
|
5
|
+
DSH ships ~40 `ctx.*` capability seams (`ctx.llm`, `ctx.tools`, `ctx.subagents`, …) and **no voice
|
|
6
|
+
seam of any kind**. This package is the proposal and the implementation of that missing seam: a
|
|
7
|
+
provider registry over the live-session vocabulary, plus the adapter base every voice backend extends.
|
|
8
|
+
|
|
9
|
+
> **Status: staged.** This is a third-party proposal, not an official `@deepseek-ai` package. It is
|
|
10
|
+
> built to the harness's package conventions so it can be contributed upstream without restructuring.
|
|
11
|
+
|
|
12
|
+
## The model
|
|
13
|
+
|
|
14
|
+
A **provider registry**, mirroring `ctx.llm`:
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
export const name = 'realtime-openai-live'
|
|
18
|
+
export const inject = ['realtime']
|
|
19
|
+
|
|
20
|
+
export function apply(ctx: Context, config: Config) {
|
|
21
|
+
ctx.realtime.registerAdapter(['openai-live'], new OpenAiLiveAdapter(config))
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The seam is deliberately thin. It owns three things:
|
|
26
|
+
|
|
27
|
+
| Owned here | Owned by the adapter |
|
|
28
|
+
|---|---|
|
|
29
|
+
| Route registration, all-or-nothing, disposed with the fiber | The transport (WebSocket, WebRTC, in-process) |
|
|
30
|
+
| Provider metadata and advisory model catalogues | Wire encoding and decoding |
|
|
31
|
+
| Append bounds (`MAX_APPEND_CHARS`) and coded failures | Token accounting and provider error mapping |
|
|
32
|
+
|
|
33
|
+
## What the seam deliberately does not have
|
|
34
|
+
|
|
35
|
+
- **No end-of-utterance call.** Endpointing belongs to the voice provider. A client-side detector
|
|
36
|
+
would be a second, competing turn boundary — and the protocol family this targets has no commit
|
|
37
|
+
event at all, so the absence is enforced upstream, not merely preferred here.
|
|
38
|
+
- **No turn management.** Turn-taking is session state, not conversation state. `sendAudio` pushes
|
|
39
|
+
frames and callbacks deliver what happens; nothing here decides who speaks next.
|
|
40
|
+
- **No auto-answer for delegations.** `onDelegation` hands the consumer a delegation and the
|
|
41
|
+
consumer decides. There is no default path that answers on the consumer's behalf, because a
|
|
42
|
+
default that resolves work the application has not authorised is exactly the failure this design
|
|
43
|
+
refuses to make possible.
|
|
44
|
+
|
|
45
|
+
## Failure semantics
|
|
46
|
+
|
|
47
|
+
Failures that prevent a session opening are **thrown** from `session()`. Failures after a session
|
|
48
|
+
opened are delivered to `handlers.onError` — a session that dies mid-conversation must not look like
|
|
49
|
+
a rejected request. Codes are stable and branchable; message text is not part of the API.
|
|
50
|
+
|
|
51
|
+
## Registration and disposal
|
|
52
|
+
|
|
53
|
+
`registerAdapter` validates the whole candidate set before mutating anything, so a rejected
|
|
54
|
+
registration leaves the registry exactly as it was. The registration is created through `ctx.effect`,
|
|
55
|
+
so it is withdrawn with the contributing fiber — HMR unmounts routes rather than leaking them.
|
|
56
|
+
|
|
57
|
+
## Known limitations and deferred work
|
|
58
|
+
|
|
59
|
+
- **Append bounds are characters, not tokens.** The provider's real limit is 500 tokens. Counting
|
|
60
|
+
tokens exactly would bind this seam to a tokenizer it does not own, so `MAX_APPEND_CHARS` is a
|
|
61
|
+
documented conservative proxy. An adapter that can count tokens should tighten it rather than
|
|
62
|
+
widening the seam's promise.
|
|
63
|
+
- **No session resumption.** The protocol fixes provider, model, voice and delegation mode at
|
|
64
|
+
startup; a seam-level `resume` would advertise a capability the wire cannot honour.
|
|
65
|
+
- **The model catalogue is advisory.** Absence from `listModels` must never become request rejection.
|
|
66
|
+
- **Build layout deviates from the harness's.** A `@deepseek-ai` package compiles to `lib/types` and
|
|
67
|
+
ships a separately bundled `lib/index.js`; this repo compiles flat to `lib/`. The `main`/`types`
|
|
68
|
+
entries and the `.ts`-extension import style match, so the source is portable — but packaging must
|
|
69
|
+
be reconciled before an upstream PR. Tracked here rather than silently diverging.
|
package/lib/error.d.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed failures for the realtime seam, carrying stable machine codes.
|
|
3
|
+
*
|
|
4
|
+
* Codes are the seam's API: a consumer branches on `code`, never on message text. Adding a code is a
|
|
5
|
+
* minor release; changing what an existing code means is a breaking one.
|
|
6
|
+
*
|
|
7
|
+
* @module dsh-realtime/error
|
|
8
|
+
*/
|
|
9
|
+
/** Stable machine codes raised by this seam. */
|
|
10
|
+
export declare const REALTIME_ERROR_CODES: Readonly<{
|
|
11
|
+
/** A registration named an empty or malformed provider route. */
|
|
12
|
+
INVALID_PROVIDER: "INVALID_PROVIDER";
|
|
13
|
+
/** A route already has an adapter registered by another registration. */
|
|
14
|
+
DUPLICATE_PROVIDER: "DUPLICATE_PROVIDER";
|
|
15
|
+
/** Registration was released, so it can no longer replace its routes. */
|
|
16
|
+
REGISTRATION_DISPOSED: "REGISTRATION_DISPOSED";
|
|
17
|
+
/** No adapter is registered for the requested route. */
|
|
18
|
+
NO_ADAPTER: "NO_ADAPTER";
|
|
19
|
+
/** An append was empty, not a string, or over the seam's bound. */
|
|
20
|
+
INVALID_APPEND: "INVALID_APPEND";
|
|
21
|
+
/** The session has already been closed. */
|
|
22
|
+
SESSION_CLOSED: "SESSION_CLOSED";
|
|
23
|
+
/** The credential an adapter needs is absent or unusable. Names the setting, never the value. */
|
|
24
|
+
MISSING_CREDENTIAL: "MISSING_CREDENTIAL";
|
|
25
|
+
/** The provider reported a failure, or an operation it was expected to acknowledge never was. */
|
|
26
|
+
PROVIDER_ERROR: "PROVIDER_ERROR";
|
|
27
|
+
/** A recorded session could not be read as a recording. */
|
|
28
|
+
INVALID_RECORDING: "INVALID_RECORDING";
|
|
29
|
+
}>;
|
|
30
|
+
/** One of {@link REALTIME_ERROR_CODES}. */
|
|
31
|
+
export type RealtimeErrorCode = (typeof REALTIME_ERROR_CODES)[keyof typeof REALTIME_ERROR_CODES];
|
|
32
|
+
/**
|
|
33
|
+
* A typed seam failure.
|
|
34
|
+
*
|
|
35
|
+
* The constructor validates its own arguments rather than trusting callers: a failure raised while
|
|
36
|
+
* reporting a failure is the worst place to discover a malformed argument.
|
|
37
|
+
*/
|
|
38
|
+
export declare class RealtimeError extends Error {
|
|
39
|
+
/** Stable machine code. Branch on this, never on `message`. */
|
|
40
|
+
readonly code: RealtimeErrorCode;
|
|
41
|
+
/**
|
|
42
|
+
* @param message - non-empty human-readable summary. Must not contain secret material.
|
|
43
|
+
* @param code - one of {@link REALTIME_ERROR_CODES}.
|
|
44
|
+
* @param options - optional `cause`.
|
|
45
|
+
*/
|
|
46
|
+
constructor(message: string, code: RealtimeErrorCode, options?: ErrorOptions);
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=error.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../src/error.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,gDAAgD;AAChD,eAAO,MAAM,oBAAoB;IAC/B,iEAAiE;;IAEjE,yEAAyE;;IAEzE,yEAAyE;;IAEzE,wDAAwD;;IAExD,mEAAmE;;IAEnE,2CAA2C;;IAE3C,iGAAiG;;IAEjG,iGAAiG;;IAEjG,2DAA2D;;EAE3D,CAAA;AAEF,2CAA2C;AAC3C,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,OAAO,oBAAoB,CAAC,CAAA;AAEhG;;;;;GAKG;AACH,qBAAa,aAAc,SAAQ,KAAK;IACtC,+DAA+D;IAC/D,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAA;IAEhC;;;;OAIG;IACH,YAAY,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,EAAE,OAAO,CAAC,EAAE,YAAY,EAU3E;CACF"}
|
package/lib/error.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed failures for the realtime seam, carrying stable machine codes.
|
|
3
|
+
*
|
|
4
|
+
* Codes are the seam's API: a consumer branches on `code`, never on message text. Adding a code is a
|
|
5
|
+
* minor release; changing what an existing code means is a breaking one.
|
|
6
|
+
*
|
|
7
|
+
* @module dsh-realtime/error
|
|
8
|
+
*/
|
|
9
|
+
/** Stable machine codes raised by this seam. */
|
|
10
|
+
export const REALTIME_ERROR_CODES = Object.freeze({
|
|
11
|
+
/** A registration named an empty or malformed provider route. */
|
|
12
|
+
INVALID_PROVIDER: 'INVALID_PROVIDER',
|
|
13
|
+
/** A route already has an adapter registered by another registration. */
|
|
14
|
+
DUPLICATE_PROVIDER: 'DUPLICATE_PROVIDER',
|
|
15
|
+
/** Registration was released, so it can no longer replace its routes. */
|
|
16
|
+
REGISTRATION_DISPOSED: 'REGISTRATION_DISPOSED',
|
|
17
|
+
/** No adapter is registered for the requested route. */
|
|
18
|
+
NO_ADAPTER: 'NO_ADAPTER',
|
|
19
|
+
/** An append was empty, not a string, or over the seam's bound. */
|
|
20
|
+
INVALID_APPEND: 'INVALID_APPEND',
|
|
21
|
+
/** The session has already been closed. */
|
|
22
|
+
SESSION_CLOSED: 'SESSION_CLOSED',
|
|
23
|
+
/** The credential an adapter needs is absent or unusable. Names the setting, never the value. */
|
|
24
|
+
MISSING_CREDENTIAL: 'MISSING_CREDENTIAL',
|
|
25
|
+
/** The provider reported a failure, or an operation it was expected to acknowledge never was. */
|
|
26
|
+
PROVIDER_ERROR: 'PROVIDER_ERROR',
|
|
27
|
+
/** A recorded session could not be read as a recording. */
|
|
28
|
+
INVALID_RECORDING: 'INVALID_RECORDING',
|
|
29
|
+
});
|
|
30
|
+
/**
|
|
31
|
+
* A typed seam failure.
|
|
32
|
+
*
|
|
33
|
+
* The constructor validates its own arguments rather than trusting callers: a failure raised while
|
|
34
|
+
* reporting a failure is the worst place to discover a malformed argument.
|
|
35
|
+
*/
|
|
36
|
+
export class RealtimeError extends Error {
|
|
37
|
+
/** Stable machine code. Branch on this, never on `message`. */
|
|
38
|
+
code;
|
|
39
|
+
/**
|
|
40
|
+
* @param message - non-empty human-readable summary. Must not contain secret material.
|
|
41
|
+
* @param code - one of {@link REALTIME_ERROR_CODES}.
|
|
42
|
+
* @param options - optional `cause`.
|
|
43
|
+
*/
|
|
44
|
+
constructor(message, code, options) {
|
|
45
|
+
if (typeof message !== 'string' || message.length === 0) {
|
|
46
|
+
throw new TypeError('RealtimeError message must be a non-empty string');
|
|
47
|
+
}
|
|
48
|
+
if (typeof code !== 'string' || code.length === 0) {
|
|
49
|
+
throw new TypeError('RealtimeError code must be a non-empty string');
|
|
50
|
+
}
|
|
51
|
+
super(message, options);
|
|
52
|
+
this.name = 'RealtimeError';
|
|
53
|
+
this.code = code;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=error.js.map
|
package/lib/error.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error.js","sourceRoot":"","sources":["../src/error.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,gDAAgD;AAChD,MAAM,CAAC,MAAM,oBAAoB,GAAG,MAAM,CAAC,MAAM,CAAC;IAChD,iEAAiE;IACjE,gBAAgB,EAAE,kBAAkB;IACpC,yEAAyE;IACzE,kBAAkB,EAAE,oBAAoB;IACxC,yEAAyE;IACzE,qBAAqB,EAAE,uBAAuB;IAC9C,wDAAwD;IACxD,UAAU,EAAE,YAAY;IACxB,mEAAmE;IACnE,cAAc,EAAE,gBAAgB;IAChC,2CAA2C;IAC3C,cAAc,EAAE,gBAAgB;IAChC,iGAAiG;IACjG,kBAAkB,EAAE,oBAAoB;IACxC,iGAAiG;IACjG,cAAc,EAAE,gBAAgB;IAChC,2DAA2D;IAC3D,iBAAiB,EAAE,mBAAmB;CACvC,CAAC,CAAA;AAKF;;;;;GAKG;AACH,MAAM,OAAO,aAAc,SAAQ,KAAK;IACtC,+DAA+D;IACtD,IAAI,CAAmB;IAEhC;;;;OAIG;IACH,YAAY,OAAe,EAAE,IAAuB,EAAE,OAAsB;QAC1E,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACxD,MAAM,IAAI,SAAS,CAAC,kDAAkD,CAAC,CAAA;QACzE,CAAC;QACD,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAClD,MAAM,IAAI,SAAS,CAAC,+CAA+C,CAAC,CAAA;QACtE,CAAC;QACD,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;QACvB,IAAI,CAAC,IAAI,GAAG,eAAe,CAAA;QAC3B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;IAClB,CAAC;CACF"}
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Realtime voice seam: a provider registry plus the adapter base every voice backend extends.
|
|
3
|
+
*
|
|
4
|
+
* Exports the `RealtimeRuntime` service as the **default** export (a DeepSeek Harness convention for
|
|
5
|
+
* service packages) and the abstract `RealtimeAdapter` for provider backends. Function plugins
|
|
6
|
+
* must supply `name` / `inject` / `Config` / `apply` and no default export; this package is a
|
|
7
|
+
* service package, so it is mounted as a class plugin and default-exports its service.
|
|
8
|
+
*
|
|
9
|
+
* @module dsh-realtime
|
|
10
|
+
*/
|
|
11
|
+
import { Service, type Context } from '@deepseek-ai/cordis';
|
|
12
|
+
import type { RealtimeDelegation, RealtimeModelInfo, RealtimeProviderInfo, RealtimeSession, RealtimeSessionHandlers, RealtimeSessionOptions } from './types.ts';
|
|
13
|
+
export * from './types.ts';
|
|
14
|
+
export * from './error.ts';
|
|
15
|
+
export { RealtimeError, REALTIME_ERROR_CODES } from './error.ts';
|
|
16
|
+
declare module '@deepseek-ai/cordis' {
|
|
17
|
+
interface Context {
|
|
18
|
+
realtime: RealtimeRuntime;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* What {@link RealtimeRuntime.registerAdapter} returns: the disposer, plus an atomic route
|
|
23
|
+
* replacement for the same adapter instance.
|
|
24
|
+
*/
|
|
25
|
+
export interface AdapterRegistrationHandle {
|
|
26
|
+
/** Release every route this registration currently holds. */
|
|
27
|
+
(): void;
|
|
28
|
+
/**
|
|
29
|
+
* Replace this registration's routes, keeping the same adapter instance. The candidate set is
|
|
30
|
+
* validated in full first — a conflict with another registration or a malformed route throws and
|
|
31
|
+
* leaves the current routes untouched — and the swap is one synchronous section, so no observer
|
|
32
|
+
* can see the registry between release and re-registration.
|
|
33
|
+
*
|
|
34
|
+
* An empty array is legal here (a plugin whose configuration emptied holds zero routes while
|
|
35
|
+
* staying registered), unlike an empty initial registration.
|
|
36
|
+
*
|
|
37
|
+
* Throws `REGISTRATION_DISPOSED` once the registration was released: its routes are gone and its
|
|
38
|
+
* disposer has already run, so anything registered afterwards would have no owner left to release it.
|
|
39
|
+
* @param providers - the complete next route set for this registration.
|
|
40
|
+
*/
|
|
41
|
+
replace(providers: string[]): void;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Provider-wire adapter for the realtime session vocabulary.
|
|
45
|
+
*
|
|
46
|
+
* Register implementations with `ctx.realtime.registerAdapter(providers, adapter)`. The single
|
|
47
|
+
* required method is {@link session}; every other method exists so a provider can describe itself
|
|
48
|
+
* without the seam having to special-case it.
|
|
49
|
+
*/
|
|
50
|
+
export declare abstract class RealtimeAdapter {
|
|
51
|
+
/**
|
|
52
|
+
* Describe one provider route owned by this adapter.
|
|
53
|
+
* @param provider - a route passed to `registerAdapter()` for this instance.
|
|
54
|
+
* @returns detached display metadata whose `id` must equal `provider`.
|
|
55
|
+
*/
|
|
56
|
+
providerInfo(provider: string): RealtimeProviderInfo;
|
|
57
|
+
/**
|
|
58
|
+
* List the voice models this adapter can currently advertise for one owned route.
|
|
59
|
+
*
|
|
60
|
+
* The result is advisory: an adapter may accept unlisted model ids, and consumers must not turn
|
|
61
|
+
* absence into request rejection.
|
|
62
|
+
* @param _provider - one provider route owned by this adapter.
|
|
63
|
+
* @returns discoverable models in adapter-preferred order.
|
|
64
|
+
*/
|
|
65
|
+
listModels(_provider: string): Promise<readonly RealtimeModelInfo[]>;
|
|
66
|
+
/**
|
|
67
|
+
* Open one voice session. The only required method.
|
|
68
|
+
*
|
|
69
|
+
* Implementations must honor `options.signal` during establishment, and must throw (rather than
|
|
70
|
+
* resolve with a dead session) when the session cannot open.
|
|
71
|
+
* @param options - the fully-resolved request; `options.provider` selects the registered route.
|
|
72
|
+
* @returns the live session, with `options.handlers` already wired.
|
|
73
|
+
*/
|
|
74
|
+
abstract session(options: RealtimeSessionOptions): Promise<RealtimeSession>;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* The `realtime` service: an adapter registry over the session vocabulary.
|
|
78
|
+
*
|
|
79
|
+
* Registration is effect-based, so HMR or a disposal unmounts routes with the contributing fiber —
|
|
80
|
+
* there is no separate teardown path that can be forgotten.
|
|
81
|
+
*/
|
|
82
|
+
export declare class RealtimeRuntime extends Service {
|
|
83
|
+
private readonly adapters;
|
|
84
|
+
/**
|
|
85
|
+
* @param ctx - the Cordis context this service is mounted on.
|
|
86
|
+
*/
|
|
87
|
+
constructor(ctx: Context);
|
|
88
|
+
/**
|
|
89
|
+
* Register an adapter for the given provider routes, all-or-nothing.
|
|
90
|
+
*
|
|
91
|
+
* Disposed with the fiber. Throws `INVALID_PROVIDER` for a malformed route, `DUPLICATE_PROVIDER`
|
|
92
|
+
* if any route is already held by another registration.
|
|
93
|
+
* @param providers - every provider route this adapter should serve.
|
|
94
|
+
* @param adapter - the adapter that opens sessions for those routes.
|
|
95
|
+
* @returns the disposer, carrying {@link AdapterRegistrationHandle.replace}.
|
|
96
|
+
*/
|
|
97
|
+
registerAdapter(providers: string[], adapter: RealtimeAdapter): AdapterRegistrationHandle;
|
|
98
|
+
/**
|
|
99
|
+
* Validate one candidate route set for `adapter`, treating routes this registration already holds
|
|
100
|
+
* as available.
|
|
101
|
+
*
|
|
102
|
+
* Nothing is mutated: a rejected candidate leaves the registry exactly as it was, which is what
|
|
103
|
+
* makes {@link AdapterRegistrationHandle.replace} a swap rather than a delete-then-add that can
|
|
104
|
+
* strand the registry empty.
|
|
105
|
+
* @param providers - candidate routes.
|
|
106
|
+
* @param adapter - the adapter that would own them.
|
|
107
|
+
* @param owned - routes this same registration already holds.
|
|
108
|
+
* @returns registrations ready to commit.
|
|
109
|
+
*/
|
|
110
|
+
private prepare;
|
|
111
|
+
/**
|
|
112
|
+
* Swap this registration's routes for the prepared ones in one synchronous section, so no
|
|
113
|
+
* observer can see the registry between the release and the re-registration.
|
|
114
|
+
* @param owned - mutable set tracking which routes this registration holds.
|
|
115
|
+
* @param registrations - validated registrations to install.
|
|
116
|
+
*/
|
|
117
|
+
private commit;
|
|
118
|
+
/**
|
|
119
|
+
* Describe the provider routes that currently have an adapter.
|
|
120
|
+
* @returns detached provider metadata in registration order.
|
|
121
|
+
*/
|
|
122
|
+
listProviders(): RealtimeProviderInfo[];
|
|
123
|
+
/**
|
|
124
|
+
* Resolve the adapter owning one route.
|
|
125
|
+
* @param provider - registered route to look up.
|
|
126
|
+
* @returns that route's registration.
|
|
127
|
+
* @throws RealtimeError `NO_ADAPTER` when the route is unregistered.
|
|
128
|
+
*/
|
|
129
|
+
private registration;
|
|
130
|
+
/**
|
|
131
|
+
* Discover the voice models one registered route advertises.
|
|
132
|
+
* @param provider - registered route to inspect.
|
|
133
|
+
* @returns detached model metadata in adapter-preferred order, duplicates removed.
|
|
134
|
+
*/
|
|
135
|
+
listModels(provider: string): Promise<RealtimeModelInfo[]>;
|
|
136
|
+
/**
|
|
137
|
+
* Open one voice session through the adapter registered for its route.
|
|
138
|
+
*
|
|
139
|
+
* A field this seam cannot honor is rejected here rather than forwarded as a no-op — the caller
|
|
140
|
+
* learns the request is unsupported before a live conversation depends on it.
|
|
141
|
+
* @param options - the session request; `options.provider` selects the adapter.
|
|
142
|
+
* @returns the adapter's live session.
|
|
143
|
+
* @throws RealtimeError `NO_ADAPTER` for an unregistered route.
|
|
144
|
+
*/
|
|
145
|
+
session(options: RealtimeSessionOptions): Promise<RealtimeSession>;
|
|
146
|
+
/**
|
|
147
|
+
* Validate one context append against the seam's bounds.
|
|
148
|
+
*
|
|
149
|
+
* Exposed so an adapter can enforce the same bound the seam promises, instead of each backend
|
|
150
|
+
* re-deriving it. Bound enforcement lives at the operation that makes the decision: a caller that
|
|
151
|
+
* bypasses this cannot silently send an over-long append.
|
|
152
|
+
* @param content - candidate append text.
|
|
153
|
+
* @param maxChars - character ceiling; defaults to the provider's documented bound.
|
|
154
|
+
* @returns the content unchanged, for convenient inline use.
|
|
155
|
+
* @throws RealtimeError `INVALID_APPEND` for a non-string, empty, or over-long value.
|
|
156
|
+
*/
|
|
157
|
+
static assertAppendable(content: string, maxChars: number): string;
|
|
158
|
+
}
|
|
159
|
+
/** Re-exported so adapters can type their handler wiring without a second import. */
|
|
160
|
+
export type { RealtimeDelegation, RealtimeSessionHandlers };
|
|
161
|
+
export default RealtimeRuntime;
|
|
162
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAE3D,OAAO,KAAK,EACV,kBAAkB,EAClB,iBAAiB,EACjB,oBAAoB,EACpB,eAAe,EACf,uBAAuB,EACvB,sBAAsB,EACvB,MAAM,YAAY,CAAA;AAEnB,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA;AAEhE,OAAO,QAAQ,qBAAqB,CAAC;IACnC,UAAU,OAAO;QACf,QAAQ,EAAE,eAAe,CAAA;KAC1B;CACF;AAED;;;GAGG;AACH,MAAM,WAAW,yBAAyB;IACxC,6DAA6D;IAC7D,IAAI,IAAI,CAAA;IACR;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI,CAAA;CACnC;AAED;;;;;;GAMG;AACH,8BAAsB,eAAe;IACnC;;;;OAIG;IACH,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,oBAAoB,CAEnD;IAED;;;;;;;OAOG;IACH,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,iBAAiB,EAAE,CAAC,CAEnE;IAED;;;;;;;OAOG;IACH,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,eAAe,CAAC,CAAA;CAC5E;AAQD;;;;;GAKG;AACH,qBAAa,eAAgB,SAAQ,OAAO;IAC1C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAyC;IAElE;;OAEG;IACH,YAAY,GAAG,EAAE,OAAO,EAEvB;IAED;;;;;;;;OAQG;IACH,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,eAAe,GAAG,yBAAyB,CAiCxF;IAED;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,OAAO;IAgCf;;;;;OAKG;IACH,OAAO,CAAC,MAAM;IASd;;;OAGG;IACH,aAAa,IAAI,oBAAoB,EAAE,CAEtC;IAED;;;;;OAKG;IACH,OAAO,CAAC,YAAY;IAQpB;;;;OAIG;IACG,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAe/D;IAED;;;;;;;;OAQG;IACG,OAAO,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,eAAe,CAAC,CAgBvE;IAED;;;;;;;;;;OAUG;IACH,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAWjE;CACF;AAED,qFAAqF;AACrF,YAAY,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,CAAA;eAE5C,eAAe"}
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Realtime voice seam: a provider registry plus the adapter base every voice backend extends.
|
|
3
|
+
*
|
|
4
|
+
* Exports the `RealtimeRuntime` service as the **default** export (a DeepSeek Harness convention for
|
|
5
|
+
* service packages) and the abstract `RealtimeAdapter` for provider backends. Function plugins
|
|
6
|
+
* must supply `name` / `inject` / `Config` / `apply` and no default export; this package is a
|
|
7
|
+
* service package, so it is mounted as a class plugin and default-exports its service.
|
|
8
|
+
*
|
|
9
|
+
* @module dsh-realtime
|
|
10
|
+
*/
|
|
11
|
+
import { Service } from '@deepseek-ai/cordis';
|
|
12
|
+
import { REALTIME_ERROR_CODES, RealtimeError } from './error.js';
|
|
13
|
+
export * from './types.js';
|
|
14
|
+
export * from './error.js';
|
|
15
|
+
export { RealtimeError, REALTIME_ERROR_CODES } from './error.js';
|
|
16
|
+
/**
|
|
17
|
+
* Provider-wire adapter for the realtime session vocabulary.
|
|
18
|
+
*
|
|
19
|
+
* Register implementations with `ctx.realtime.registerAdapter(providers, adapter)`. The single
|
|
20
|
+
* required method is {@link session}; every other method exists so a provider can describe itself
|
|
21
|
+
* without the seam having to special-case it.
|
|
22
|
+
*/
|
|
23
|
+
export class RealtimeAdapter {
|
|
24
|
+
/**
|
|
25
|
+
* Describe one provider route owned by this adapter.
|
|
26
|
+
* @param provider - a route passed to `registerAdapter()` for this instance.
|
|
27
|
+
* @returns detached display metadata whose `id` must equal `provider`.
|
|
28
|
+
*/
|
|
29
|
+
providerInfo(provider) {
|
|
30
|
+
return { id: provider, name: provider };
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* List the voice models this adapter can currently advertise for one owned route.
|
|
34
|
+
*
|
|
35
|
+
* The result is advisory: an adapter may accept unlisted model ids, and consumers must not turn
|
|
36
|
+
* absence into request rejection.
|
|
37
|
+
* @param _provider - one provider route owned by this adapter.
|
|
38
|
+
* @returns discoverable models in adapter-preferred order.
|
|
39
|
+
*/
|
|
40
|
+
listModels(_provider) {
|
|
41
|
+
return Promise.resolve([]);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The `realtime` service: an adapter registry over the session vocabulary.
|
|
46
|
+
*
|
|
47
|
+
* Registration is effect-based, so HMR or a disposal unmounts routes with the contributing fiber —
|
|
48
|
+
* there is no separate teardown path that can be forgotten.
|
|
49
|
+
*/
|
|
50
|
+
export class RealtimeRuntime extends Service {
|
|
51
|
+
adapters = new Map();
|
|
52
|
+
/**
|
|
53
|
+
* @param ctx - the Cordis context this service is mounted on.
|
|
54
|
+
*/
|
|
55
|
+
constructor(ctx) {
|
|
56
|
+
super(ctx, 'realtime');
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Register an adapter for the given provider routes, all-or-nothing.
|
|
60
|
+
*
|
|
61
|
+
* Disposed with the fiber. Throws `INVALID_PROVIDER` for a malformed route, `DUPLICATE_PROVIDER`
|
|
62
|
+
* if any route is already held by another registration.
|
|
63
|
+
* @param providers - every provider route this adapter should serve.
|
|
64
|
+
* @param adapter - the adapter that opens sessions for those routes.
|
|
65
|
+
* @returns the disposer, carrying {@link AdapterRegistrationHandle.replace}.
|
|
66
|
+
*/
|
|
67
|
+
registerAdapter(providers, adapter) {
|
|
68
|
+
if (providers.length === 0) {
|
|
69
|
+
throw new RealtimeError('an adapter must register at least one provider', REALTIME_ERROR_CODES.INVALID_PROVIDER);
|
|
70
|
+
}
|
|
71
|
+
// Routes this registration currently holds; `replace` rewrites it, and the disposer releases
|
|
72
|
+
// whatever it holds at disposal time.
|
|
73
|
+
const owned = new Set();
|
|
74
|
+
// `owned` being empty cannot report disposal on its own, because `replace([])` legally leaves a
|
|
75
|
+
// live registration holding none.
|
|
76
|
+
let released = false;
|
|
77
|
+
const dispose = this.ctx.effect(function* () {
|
|
78
|
+
this.commit(owned, this.prepare(providers, adapter, owned));
|
|
79
|
+
yield () => {
|
|
80
|
+
released = true;
|
|
81
|
+
for (const provider of owned)
|
|
82
|
+
this.adapters.delete(provider);
|
|
83
|
+
owned.clear();
|
|
84
|
+
};
|
|
85
|
+
}.bind(this), 'realtime.registerAdapter()');
|
|
86
|
+
const handle = (() => void dispose());
|
|
87
|
+
handle.replace = (next) => {
|
|
88
|
+
// Registering here would leak: the effect's disposer already ran, so nothing remains to
|
|
89
|
+
// release whatever this call would put in the map.
|
|
90
|
+
if (released) {
|
|
91
|
+
throw new RealtimeError('a disposed adapter registration cannot replace its routes', REALTIME_ERROR_CODES.REGISTRATION_DISPOSED);
|
|
92
|
+
}
|
|
93
|
+
this.commit(owned, this.prepare(next, adapter, owned));
|
|
94
|
+
};
|
|
95
|
+
return handle;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Validate one candidate route set for `adapter`, treating routes this registration already holds
|
|
99
|
+
* as available.
|
|
100
|
+
*
|
|
101
|
+
* Nothing is mutated: a rejected candidate leaves the registry exactly as it was, which is what
|
|
102
|
+
* makes {@link AdapterRegistrationHandle.replace} a swap rather than a delete-then-add that can
|
|
103
|
+
* strand the registry empty.
|
|
104
|
+
* @param providers - candidate routes.
|
|
105
|
+
* @param adapter - the adapter that would own them.
|
|
106
|
+
* @param owned - routes this same registration already holds.
|
|
107
|
+
* @returns registrations ready to commit.
|
|
108
|
+
*/
|
|
109
|
+
prepare(providers, adapter, owned) {
|
|
110
|
+
const unique = new Set();
|
|
111
|
+
const registrations = [];
|
|
112
|
+
for (const provider of providers) {
|
|
113
|
+
if (typeof provider !== 'string' || provider.length === 0) {
|
|
114
|
+
throw new RealtimeError('adapter provider names must be non-empty strings', REALTIME_ERROR_CODES.INVALID_PROVIDER);
|
|
115
|
+
}
|
|
116
|
+
if (unique.has(provider) || (this.adapters.has(provider) && !owned.has(provider))) {
|
|
117
|
+
throw new RealtimeError(`an adapter for provider "${provider}" is already registered`, REALTIME_ERROR_CODES.DUPLICATE_PROVIDER);
|
|
118
|
+
}
|
|
119
|
+
const info = adapter.providerInfo(provider);
|
|
120
|
+
if (typeof info.id !== 'string' || info.id !== provider
|
|
121
|
+
|| typeof info.name !== 'string' || info.name.length === 0) {
|
|
122
|
+
throw new RealtimeError(`adapter metadata for provider "${provider}" must preserve its id and have a non-empty name`, REALTIME_ERROR_CODES.INVALID_PROVIDER);
|
|
123
|
+
}
|
|
124
|
+
unique.add(provider);
|
|
125
|
+
registrations.push({
|
|
126
|
+
adapter,
|
|
127
|
+
provider: info.description === undefined
|
|
128
|
+
? { id: info.id, name: info.name }
|
|
129
|
+
: { id: info.id, name: info.name, description: info.description },
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
return registrations;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Swap this registration's routes for the prepared ones in one synchronous section, so no
|
|
136
|
+
* observer can see the registry between the release and the re-registration.
|
|
137
|
+
* @param owned - mutable set tracking which routes this registration holds.
|
|
138
|
+
* @param registrations - validated registrations to install.
|
|
139
|
+
*/
|
|
140
|
+
commit(owned, registrations) {
|
|
141
|
+
for (const provider of owned)
|
|
142
|
+
this.adapters.delete(provider);
|
|
143
|
+
owned.clear();
|
|
144
|
+
for (const registration of registrations) {
|
|
145
|
+
this.adapters.set(registration.provider.id, registration);
|
|
146
|
+
owned.add(registration.provider.id);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Describe the provider routes that currently have an adapter.
|
|
151
|
+
* @returns detached provider metadata in registration order.
|
|
152
|
+
*/
|
|
153
|
+
listProviders() {
|
|
154
|
+
return [...this.adapters.values()].map(({ provider }) => ({ ...provider }));
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Resolve the adapter owning one route.
|
|
158
|
+
* @param provider - registered route to look up.
|
|
159
|
+
* @returns that route's registration.
|
|
160
|
+
* @throws RealtimeError `NO_ADAPTER` when the route is unregistered.
|
|
161
|
+
*/
|
|
162
|
+
registration(provider) {
|
|
163
|
+
const registration = this.adapters.get(provider);
|
|
164
|
+
if (registration === undefined) {
|
|
165
|
+
throw new RealtimeError(`no realtime adapter registered for provider "${provider}"`, REALTIME_ERROR_CODES.NO_ADAPTER);
|
|
166
|
+
}
|
|
167
|
+
return registration;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Discover the voice models one registered route advertises.
|
|
171
|
+
* @param provider - registered route to inspect.
|
|
172
|
+
* @returns detached model metadata in adapter-preferred order, duplicates removed.
|
|
173
|
+
*/
|
|
174
|
+
async listModels(provider) {
|
|
175
|
+
const models = await this.registration(provider).adapter.listModels(provider);
|
|
176
|
+
const seen = new Set();
|
|
177
|
+
const detached = [];
|
|
178
|
+
for (const model of models) {
|
|
179
|
+
if (typeof model.id !== 'string' || model.id.length === 0 || seen.has(model.id))
|
|
180
|
+
continue;
|
|
181
|
+
seen.add(model.id);
|
|
182
|
+
detached.push({
|
|
183
|
+
id: model.id,
|
|
184
|
+
name: typeof model.name === 'string' && model.name.length > 0 ? model.name : model.id,
|
|
185
|
+
...model.inputModalities === undefined ? {} : { inputModalities: [...model.inputModalities] },
|
|
186
|
+
...model.outputModalities === undefined ? {} : { outputModalities: [...model.outputModalities] },
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
return detached;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Open one voice session through the adapter registered for its route.
|
|
193
|
+
*
|
|
194
|
+
* A field this seam cannot honor is rejected here rather than forwarded as a no-op — the caller
|
|
195
|
+
* learns the request is unsupported before a live conversation depends on it.
|
|
196
|
+
* @param options - the session request; `options.provider` selects the adapter.
|
|
197
|
+
* @returns the adapter's live session.
|
|
198
|
+
* @throws RealtimeError `NO_ADAPTER` for an unregistered route.
|
|
199
|
+
*/
|
|
200
|
+
async session(options) {
|
|
201
|
+
if (typeof options.provider !== 'string' || options.provider.length === 0) {
|
|
202
|
+
throw new RealtimeError('a session needs a non-empty provider route', REALTIME_ERROR_CODES.INVALID_PROVIDER);
|
|
203
|
+
}
|
|
204
|
+
if (typeof options.model !== 'string' || options.model.length === 0) {
|
|
205
|
+
throw new RealtimeError('a session needs a non-empty model id', REALTIME_ERROR_CODES.INVALID_PROVIDER);
|
|
206
|
+
}
|
|
207
|
+
if (options.instructions !== undefined && options.instructions.length === 0) {
|
|
208
|
+
throw new RealtimeError('session instructions must be non-empty when supplied', REALTIME_ERROR_CODES.INVALID_APPEND);
|
|
209
|
+
}
|
|
210
|
+
if (options.signal?.aborted) {
|
|
211
|
+
throw new RealtimeError('session establishment was aborted before it started', REALTIME_ERROR_CODES.SESSION_CLOSED, {
|
|
212
|
+
cause: options.signal.reason,
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
return await this.registration(options.provider).adapter.session(options);
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Validate one context append against the seam's bounds.
|
|
219
|
+
*
|
|
220
|
+
* Exposed so an adapter can enforce the same bound the seam promises, instead of each backend
|
|
221
|
+
* re-deriving it. Bound enforcement lives at the operation that makes the decision: a caller that
|
|
222
|
+
* bypasses this cannot silently send an over-long append.
|
|
223
|
+
* @param content - candidate append text.
|
|
224
|
+
* @param maxChars - character ceiling; defaults to the provider's documented bound.
|
|
225
|
+
* @returns the content unchanged, for convenient inline use.
|
|
226
|
+
* @throws RealtimeError `INVALID_APPEND` for a non-string, empty, or over-long value.
|
|
227
|
+
*/
|
|
228
|
+
static assertAppendable(content, maxChars) {
|
|
229
|
+
if (typeof content !== 'string' || content.length === 0) {
|
|
230
|
+
throw new RealtimeError('an append needs non-empty content', REALTIME_ERROR_CODES.INVALID_APPEND);
|
|
231
|
+
}
|
|
232
|
+
if (content.length > maxChars) {
|
|
233
|
+
throw new RealtimeError(`an append of ${content.length} characters exceeds the ${maxChars}-character seam bound`, REALTIME_ERROR_CODES.INVALID_APPEND);
|
|
234
|
+
}
|
|
235
|
+
return content;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
export default RealtimeRuntime;
|
|
239
|
+
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|