@vibedev-si/dsh-vibedev 0.3.0 → 0.3.2
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/README.md +28 -10
- package/THIRD-PARTY-NOTICES.txt +1 -1
- package/client/client.js +56 -5
- package/lib/account/index.d.ts +50 -1
- package/lib/audio/request.d.ts +141 -0
- package/lib/gateway/assets.d.ts +2 -2
- package/lib/gateway/catalog.d.ts +75 -4
- package/lib/gateway/http.d.ts +5 -0
- package/lib/index.d.ts +7 -2
- package/lib/index.js +1211 -160
- package/lib/llm/adapter.d.ts +2 -0
- package/lib/llm/index.d.ts +2 -0
- package/lib/pricing.d.ts +45 -9
- package/lib/runtime.d.ts +24 -1
- package/lib/service.d.ts +81 -7
- package/lib/tasks/remote.d.ts +13 -0
- package/lib/tasks/store.d.ts +17 -0
- package/lib/tasks/tracker.d.ts +15 -0
- package/lib/tools/audio.d.ts +3 -2
- package/lib/tools/models.d.ts +7 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
在 DeepSeek Harness 里登录 VibeDev 账号,和 DeepSeek 账号并存:
|
|
4
4
|
|
|
5
5
|
- **VibeDev 的模型**:登录后,模型选择器里会多出「VibeDev」一组(Claude、GPT 等网关提供的模型),和 DeepSeek 的模型并列,选中就能用;
|
|
6
|
-
- **媒体生成**:Agent
|
|
6
|
+
- **媒体生成**:Agent 可以生成图片、视频、音乐、播客和语音,并把语音转成文字,结果保存在工作区的 `media/` 目录下;
|
|
7
7
|
- **网页搜索**:提供走 VibeDev 网关的网页搜索,见下面的「网页搜索」。
|
|
8
8
|
|
|
9
9
|
费用都从你的 VibeDev 余额里扣,DeepSeek 账号照常由 DeepSeek Harness 自己管理,两边互不影响。还没有 VibeDev 账号的话,可以在 [VibeDev](https://api.vibedev.studio) 注册。
|
|
@@ -65,8 +65,8 @@
|
|
|
65
65
|
| --- | --- |
|
|
66
66
|
| `image_generate` | 生成图片;带参考图时用来改图、换风格或把产品放进场景。生成的图片 Agent 自己能看到。 |
|
|
67
67
|
| `video_generate` | 生成视频,在后台运行,一般 1 到 10 分钟。完成后会通知 Agent,文件保存在 `media/videos/`。 |
|
|
68
|
-
| `audio_generate` |
|
|
69
|
-
| `audio_transcribe` |
|
|
68
|
+
| `audio_generate` | 生成音乐(可以自带歌词)、一期播客(围绕一个话题或一个网页),或一段语音(朗读文字,可以用参考音频指定音色)。 |
|
|
69
|
+
| `audio_transcribe` | 把语音转成文字(以文件上传,语言和时长限制以模型声明为准,目前常见为普通话)。 |
|
|
70
70
|
| `media_models` | 列出账号可用的媒体模型和价格;视频模型还会列出每种模式接受哪些输入。 |
|
|
71
71
|
| `media_tasks` | 查看进行中和已完成的视频、音频任务。 |
|
|
72
72
|
| `media_account` | 查看用的是哪个 VibeDev 账号;登录或退出(和侧边栏、设置里的登录是同一个)。 |
|
|
@@ -81,11 +81,23 @@
|
|
|
81
81
|
|
|
82
82
|
每个视频模型支持的模式、参考素材数量、时长、画面比例和分辨率都不一样。插件会按网关下发的模型能力逐项检查:模型不支持的请求,会在上传和扣费之前就被拒绝,并说明这个模型支持什么。所有参考素材会先上传到 VibeDev 网关的素材库,网关会检查格式,并测量参考视频的时长。
|
|
83
83
|
|
|
84
|
+
### 音频模型
|
|
85
|
+
|
|
86
|
+
音频模型是音乐、播客、语音还是转写,只看网关模型目录里 `audio.generation_kind` 的声明,不按模型名称猜;没有声明类型的模型会列出来,但不能用来生成。每个模型只提交它在 `request_fields` 里声明的字段,正文写进 `prompt_field` 指定的字段:
|
|
87
|
+
|
|
88
|
+
- 音乐:`prompt`、`lyrics`、`genre`、`mood`、`gender`;
|
|
89
|
+
- 播客:`prompt`、`source_url`;
|
|
90
|
+
- 语音:按模型声明,例如 Seed 是 `text_prompt`、`references`(参考音频)、`audio_config`(输出格式和采样率)、`watermark`。
|
|
91
|
+
|
|
92
|
+
参考音频的数量、大小、时长、不能同时使用的字段,以及 WAV、MP3 等格式各自支持的采样率,都以网关下发的能力为准。音乐和播客不接受参考音频、格式、采样率或时长参数。
|
|
93
|
+
|
|
84
94
|
## 费用
|
|
85
95
|
|
|
86
96
|
- VibeDev 的聊天模型按用量计费,价格以 VibeDev 网关为准。
|
|
87
|
-
-
|
|
88
|
-
-
|
|
97
|
+
- 图片按张计费,视频按秒计费(参考视频的时长也计入),音乐和播客按次计费,语音按原始秒计费(按服务商返回的原始时长,可能和播放时长不同),转写按分钟计费。`media_models` 会列出当前价格;目录没有给出价格的模型显示「未定价」,不代表免费。
|
|
98
|
+
- 视频和音频只在成功时扣费,失败和取消的任务不扣。视频提交后无法取消。
|
|
99
|
+
- 同一次操作只会生成一次:提交没有得到确认时,会用同一个请求键向网关确认结果;查询或下载失败只会重新查询、刷新链接,不会重新提交。结果已经生成、但费用还在结算(`settlement_pending`)时,结果照常保存,不会重新生成。
|
|
100
|
+
- 余额在每次付费请求结束后(对话回复、生成、转写)马上重新读取,侧栏和设置页几秒内就能看到扣费后的余额。
|
|
89
101
|
- 插件设置里的「每次付费生成前先询问」默认关闭。打开后,每次付费的媒体请求之前都会先弹出确认,并显示预估金额。
|
|
90
102
|
|
|
91
103
|
## 设置
|
|
@@ -96,7 +108,7 @@
|
|
|
96
108
|
| --- | --- | --- |
|
|
97
109
|
| 每次付费生成前先询问 | 关 | 打开后,每次媒体生成前先确认费用。 |
|
|
98
110
|
| 保存目录 | `media` | 相对于工作区。 |
|
|
99
|
-
| 默认生图 / 视频 / 音乐 / 播客 / 转写模型 | 空 | 留空时由 Agent
|
|
111
|
+
| 默认生图 / 视频 / 音乐 / 播客 / 语音合成 / 转写模型 | 空 | 留空时由 Agent 按模型目录选择;可以填模型 id 或别名。 |
|
|
100
112
|
|
|
101
113
|
另有几项只能在配置文件(`cordis.patch.yml` 中 `dsh-vibedev` 一行)里改:`displayName`(模型选择器里显示的组名,默认 `VibeDev`)、`preferredModels`(排在最前的 VibeDev 模型)、`openBrowserOnSignIn`(登录时是否自动打开系统浏览器,默认开)、`catalogRefreshMinutes`(模型目录刷新间隔,默认 30 分钟)、`client`(登录时报给网关的客户端名称,默认 `vibedev-plugin`)、`primary`(作为应用的主账号显示,默认关,VibeDev 应用里开)。
|
|
102
114
|
|
|
@@ -137,7 +149,7 @@ npm run build
|
|
|
137
149
|
Sign in to VibeDev in DeepSeek Harness, next to the DeepSeek account:
|
|
138
150
|
|
|
139
151
|
- **VibeDev models**: once signed in, the model picker gains a "VibeDev" group (Claude, GPT and the other models of the VibeDev gateway) next to DeepSeek's;
|
|
140
|
-
- **Media generation**: the agent generates images, videos, music and
|
|
152
|
+
- **Media generation**: the agent generates images, videos, music, podcasts and speech, and transcribes speech; results are saved in the workspace under `media/`.
|
|
141
153
|
|
|
142
154
|
Everything is paid from your VibeDev balance; the DeepSeek account stays DeepSeek Harness's own, and the two do not affect each other. To create an account, register at [VibeDev](https://api.vibedev.studio). This plugin was renamed from `dsh-media`.
|
|
143
155
|
|
|
@@ -149,13 +161,19 @@ Everything is paid from your VibeDev balance; the DeepSeek account stays DeepSee
|
|
|
149
161
|
|
|
150
162
|
**Creator tools withdrawn (0.2.5)**: the Creator tools line 0.2.4 added at the end of Settings → VibeDev account is gone. It was a cross-plugin recommendation slot, and in the settings page it hurt the layout; a redesign can revisit it later. Only that interface change is reverted: the account, the balance, the model list, the sign-in and saved screenplays, boards and film files are untouched.
|
|
151
163
|
|
|
164
|
+
**Gateway audio (0.3.1)**: audio models are recognized from the catalog's structured `audio` block (music, podcast, speech, transcription), never from their names; speech generation (Seed) joins music and podcasts; prices show their unit (per generation, per second of original audio, per minute) and "not priced" when the catalog states none; the `vibedevMedia` service gains `startAudio` with an operation key and query-only result reading.
|
|
165
|
+
|
|
166
|
+
**Balance without the wait (0.3.2)**: the balance is read again right after a paid request ends (a chat reply, a generation, a transcription) and the account pages hear it through a long poll, so a charge shows within seconds instead of after up to two minutes. A page that already has a balance is answered at once while a newer one is read behind it; reads after charges are at least five seconds apart.
|
|
167
|
+
|
|
152
168
|
**Models**: the list comes from the VibeDev gateway for your account, so nothing is listed before you sign in; models appear right after, without a restart. Using a VibeDev model while signed out, or after the sign-in has ended, asks you to sign in; a used-up balance says so with the top-up link. These messages are separate from the DeepSeek account's. An expired access token is renewed and the request sent again, without repeating any output.
|
|
153
169
|
|
|
154
170
|
**Web search**: the plugin registers the web search provider `vibedev-gateway`, signed in with your VibeDev account. Registering does not select it: DeepSeek Harness keeps its own search unless the profile sets `searchProvider: vibedev-gateway` on the `web` row (the VibeDev app does). Searching is free, but the gateway refuses every request, searches included, while the balance is zero.
|
|
155
171
|
|
|
156
|
-
**Media tools**: `image_generate`, `video_generate` (runs as a background job), `audio_generate` (music
|
|
172
|
+
**Media tools**: `image_generate`, `video_generate` (runs as a background job), `audio_generate` (music, a podcast or speech), `audio_transcribe` (a multipart file upload; languages and limits as each model declares), `media_models`, `media_tasks` and `media_account`. Reference images, videos and audio, and first and last frames, can each be a workspace path, an http(s) link, or `chat:1` for the most recent matching file you attached. A request the model cannot serve is refused before anything is uploaded or charged.
|
|
173
|
+
|
|
174
|
+
**Audio models**: whether an audio model makes music, a podcast, speech or a transcription comes from its catalog declaration (`audio.generation_kind`), never from its name; a model that declares no kind is listed but cannot generate. Each model is sent only the fields it declares in `request_fields`, with the text in its `prompt_field`: music takes `prompt`, `lyrics`, `genre`, `mood` and `gender`; a podcast `prompt` and `source_url`; speech what it declares (Seed: `text_prompt`, `references`, `audio_config`, `watermark`). Speech reference limits (count, size, length, exclusions) and the sample rates of each output format come from the gateway. Music and podcasts take no references, format, sample rate or duration.
|
|
157
175
|
|
|
158
|
-
**Cost**: chat models are billed by usage at the gateway's prices; images per image, video per second (reference video length counts), music and podcasts per request. Failed video and audio tasks are not charged, and a submitted video cannot be cancelled. "Ask before each paid generation" is off by default.
|
|
176
|
+
**Cost**: chat models are billed by usage at the gateway's prices; images per image, video per second (reference video length counts), music and podcasts per request, speech per second of original audio (the length the provider reports, which can differ from playback), transcription per minute. A model without a catalog price is shown as "not priced" (未定价), never as free. Failed and cancelled video and audio tasks are not charged, and a submitted video cannot be cancelled. One operation generates once: an unconfirmed submission is settled under the same key, a failed query or download only queries again or renews the link, and a result whose charge is still settling (`settlement_pending`) is kept, never generated again. "Ask before each paid generation" is off by default.
|
|
159
177
|
|
|
160
178
|
**Settings**: open "VibeDev Account & Models" on the Plugins page for the spending confirmation, the output folder and the default media models. `displayName`, `preferredModels`, `openBrowserOnSignIn`, `catalogRefreshMinutes`, `client` (the client name sent with the sign-in, default `vibedev-plugin`) and `primary` (show the account as the app's main account; off by default, on in the VibeDev app) are set in the profile's `cordis.patch.yml` row `dsh-vibedev`.
|
|
161
179
|
|
|
@@ -163,7 +181,7 @@ Everything is paid from your VibeDev balance; the DeepSeek account stays DeepSee
|
|
|
163
181
|
|
|
164
182
|
**Privacy**: prompts and reference media are sent to the VibeDev gateway, which forwards them to the model provider. The plugin's own sign-in is kept in the DeepSeek Harness credential store, and task records under `dsh-media/` in the DeepSeek Harness data folder (the former name, kept so an upgrade loses nothing).
|
|
165
183
|
|
|
166
|
-
**For other plugins**: the `vibedevMedia` service generates images and
|
|
184
|
+
**For other plugins**: the `vibedevMedia` service generates images, videos and audio (`startAudio`: music, podcasts and speech) through the same model choice, reference checks, idempotent submission and background saving; the film workbench ([dsh-film](https://github.com/VibeDev-Si/dsh-film)) storyboard uses it. `audioForms()` describes the form of each audio model (only its declared fields, choices and limits, and its price). Every task is read the same way: `task` (as recorded), `refreshTask` (ask the gateway again — fresh links, settlement; a query, never a submission), `onTask` (each change) and `result` (the files to play or download). Pass the same `operationId` when one click is retried: its task is returned and nothing is submitted twice.
|
|
167
185
|
|
|
168
186
|
**Third-party code**: the model route in `src/llm/` is ported from the VibeDev app; `context.ts`, `replay.ts` and `stream.ts` are copied from DeepSeek Harness's `@deepseek-ai/dsh-llm-pi-ai` (MIT). The licences of the bundled pi-ai, Anthropic SDK, OpenAI SDK and their dependencies are in `THIRD-PARTY-NOTICES.txt`.
|
|
169
187
|
|
package/THIRD-PARTY-NOTICES.txt
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
THIRD-PARTY NOTICES · @vibedev-si/dsh-vibedev 0.3.
|
|
1
|
+
THIRD-PARTY NOTICES · @vibedev-si/dsh-vibedev 0.3.2
|
|
2
2
|
|
|
3
3
|
This package bundles code from the projects below. Each is used under the licence that
|
|
4
4
|
follows its name; the licence texts are reproduced as the projects ship them.
|
package/client/client.js
CHANGED
|
@@ -9,6 +9,8 @@ window.__ModuleLoader__.load({ id: "@vibedev-si/dsh-vibedev", factory: (require)
|
|
|
9
9
|
const ACCOUNT_ROUTE = "/api/dsh-vibedev/account";
|
|
10
10
|
const PENDING_POLL_MS = 2e3;
|
|
11
11
|
const IDLE_POLL_MS = 6e4;
|
|
12
|
+
/** After a failed long poll, the next one waits this long (the Host may be restarting). */
|
|
13
|
+
const WATCH_RETRY_MS = 5e3;
|
|
12
14
|
/** Who to show: the nickname, else the email. */
|
|
13
15
|
function displayName(view) {
|
|
14
16
|
return view?.user?.nickname ?? view?.user?.email;
|
|
@@ -33,6 +35,8 @@ window.__ModuleLoader__.load({ id: "@vibedev-si/dsh-vibedev", factory: (require)
|
|
|
33
35
|
reading;
|
|
34
36
|
stopped = true;
|
|
35
37
|
unsubscribeFocus;
|
|
38
|
+
/** The open long poll, when one is. */
|
|
39
|
+
watching;
|
|
36
40
|
/**
|
|
37
41
|
* @param options - fetch, timers, window opening and focus (test seams).
|
|
38
42
|
*/
|
|
@@ -82,17 +86,61 @@ window.__ModuleLoader__.load({ id: "@vibedev-si/dsh-vibedev", factory: (require)
|
|
|
82
86
|
globalThis.clearTimeout(handle);
|
|
83
87
|
}))(this.timer);
|
|
84
88
|
this.timer = void 0;
|
|
89
|
+
this.watching?.abort();
|
|
90
|
+
this.watching = void 0;
|
|
85
91
|
}
|
|
86
|
-
|
|
87
|
-
if (this.stopped) return;
|
|
92
|
+
later(callback, ms) {
|
|
88
93
|
if (this.timer !== void 0) (this.options.clearTimeout ?? ((handle) => {
|
|
89
94
|
globalThis.clearTimeout(handle);
|
|
90
95
|
}))(this.timer);
|
|
91
|
-
|
|
92
|
-
|
|
96
|
+
this.timer = (this.options.setTimeout ?? ((run, delay) => globalThis.setTimeout(run, delay)))(() => {
|
|
97
|
+
this.timer = void 0;
|
|
98
|
+
callback();
|
|
99
|
+
}, ms);
|
|
100
|
+
}
|
|
101
|
+
schedule() {
|
|
102
|
+
if (this.stopped) return;
|
|
103
|
+
const view = this.state.view;
|
|
104
|
+
if (view?.pending === void 0 && typeof view?.revision === "number") {
|
|
105
|
+
if (this.timer !== void 0) (this.options.clearTimeout ?? ((handle) => {
|
|
106
|
+
globalThis.clearTimeout(handle);
|
|
107
|
+
}))(this.timer);
|
|
93
108
|
this.timer = void 0;
|
|
109
|
+
this.watch(view.revision);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
this.watching?.abort();
|
|
113
|
+
this.watching = void 0;
|
|
114
|
+
this.later(() => {
|
|
94
115
|
this.refresh();
|
|
95
|
-
},
|
|
116
|
+
}, view?.pending !== void 0 ? PENDING_POLL_MS : IDLE_POLL_MS);
|
|
117
|
+
}
|
|
118
|
+
/** Keep one long poll open for changes past `revision`. */
|
|
119
|
+
watch(revision) {
|
|
120
|
+
if (this.watching !== void 0) return;
|
|
121
|
+
const controller = new AbortController();
|
|
122
|
+
this.watching = controller;
|
|
123
|
+
(async () => {
|
|
124
|
+
let failed = false;
|
|
125
|
+
try {
|
|
126
|
+
const response = await this.fetch(`${ACCOUNT_ROUTE}?since=${revision}`, { signal: controller.signal });
|
|
127
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
128
|
+
const view = await response.json();
|
|
129
|
+
if (!controller.signal.aborted && (view.revision ?? 0) >= (this.state.view?.revision ?? 0)) this.set({
|
|
130
|
+
view,
|
|
131
|
+
loadFailed: false
|
|
132
|
+
});
|
|
133
|
+
} catch {
|
|
134
|
+
failed = !controller.signal.aborted;
|
|
135
|
+
} finally {
|
|
136
|
+
if (this.watching === controller) this.watching = void 0;
|
|
137
|
+
}
|
|
138
|
+
if (this.stopped || controller.signal.aborted) return;
|
|
139
|
+
if (failed) this.later(() => {
|
|
140
|
+
this.refresh();
|
|
141
|
+
}, WATCH_RETRY_MS);
|
|
142
|
+
else this.schedule();
|
|
143
|
+
})();
|
|
96
144
|
}
|
|
97
145
|
/**
|
|
98
146
|
* Read the account now.
|
|
@@ -263,6 +311,7 @@ window.__ModuleLoader__.load({ id: "@vibedev-si/dsh-vibedev", factory: (require)
|
|
|
263
311
|
videoModel: "视频",
|
|
264
312
|
musicModel: "音乐",
|
|
265
313
|
podcastModel: "播客",
|
|
314
|
+
speechModel: "语音合成",
|
|
266
315
|
transcriptionModel: "语音转写",
|
|
267
316
|
modelPlaceholder: "留空自动选择",
|
|
268
317
|
account: "账号",
|
|
@@ -338,6 +387,7 @@ window.__ModuleLoader__.load({ id: "@vibedev-si/dsh-vibedev", factory: (require)
|
|
|
338
387
|
videoModel: "Video",
|
|
339
388
|
musicModel: "Music",
|
|
340
389
|
podcastModel: "Podcasts",
|
|
390
|
+
speechModel: "Speech",
|
|
341
391
|
transcriptionModel: "Speech to text",
|
|
342
392
|
modelPlaceholder: "Empty: chosen automatically",
|
|
343
393
|
account: "Account",
|
|
@@ -680,6 +730,7 @@ window.__ModuleLoader__.load({ id: "@vibedev-si/dsh-vibedev", factory: (require)
|
|
|
680
730
|
"videoModel",
|
|
681
731
|
"musicModel",
|
|
682
732
|
"podcastModel",
|
|
733
|
+
"speechModel",
|
|
683
734
|
"transcriptionModel"
|
|
684
735
|
];
|
|
685
736
|
/** Every field the page edits. */
|
package/lib/account/index.d.ts
CHANGED
|
@@ -7,6 +7,9 @@
|
|
|
7
7
|
* app's account, the plugin's own sign-in, a development key, or none), who
|
|
8
8
|
* is signed in, the balance, a sign-in waiting for the browser, and how many
|
|
9
9
|
* VibeDev models the pickers list; `?fresh=1` reads the balance again;
|
|
10
|
+
* `?since=<revision>` holds the request until the person or the balance
|
|
11
|
+
* changes (a charge is read again right after it ends), so the pages show a
|
|
12
|
+
* new balance within seconds instead of on their next minute poll;
|
|
10
13
|
* - `POST /api/dsh-vibedev/account/sign-in` — `{ open?: boolean }`: start the
|
|
11
14
|
* plugin's own sign-in; answers the authorization link (`open: false` leaves
|
|
12
15
|
* opening it to the page);
|
|
@@ -58,6 +61,11 @@ export interface AccountView {
|
|
|
58
61
|
};
|
|
59
62
|
/** The VibeDev account is the app's main account (the VibeDev app): its row leads the sidebar foot and Settings. */
|
|
60
63
|
readonly primary: boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Grows whenever the person or the balance changes; `GET …/account?since=<revision>`
|
|
66
|
+
* answers as soon as it grows (or after a while with the same view).
|
|
67
|
+
*/
|
|
68
|
+
readonly revision: number;
|
|
61
69
|
}
|
|
62
70
|
/** What the account routes need from the plugin. */
|
|
63
71
|
export interface AccountServiceOptions {
|
|
@@ -74,16 +82,51 @@ export interface AccountServiceOptions {
|
|
|
74
82
|
readonly primary?: boolean;
|
|
75
83
|
readonly fetch?: typeof globalThis.fetch;
|
|
76
84
|
readonly now?: () => number;
|
|
85
|
+
/** Timer seams for the reads after a charge and the long poll. */
|
|
86
|
+
readonly setTimeout?: (callback: () => void, ms: number) => unknown;
|
|
87
|
+
readonly clearTimeout?: (handle: unknown) => void;
|
|
77
88
|
}
|
|
78
89
|
/** The account state behind the pages. */
|
|
79
90
|
export declare class AccountService {
|
|
80
91
|
private readonly options;
|
|
81
92
|
private balanceCache;
|
|
93
|
+
/** A background read of a stale balance, so only one runs at a time. */
|
|
94
|
+
private revalidating;
|
|
82
95
|
private readonly now;
|
|
96
|
+
private revision;
|
|
97
|
+
/** What the last read showed, to tell whether the next one changed anything. */
|
|
98
|
+
private lastSeen;
|
|
99
|
+
private readonly waiters;
|
|
100
|
+
private settleTimers;
|
|
101
|
+
/** Grows with every credential change; a read started before one is dropped. */
|
|
102
|
+
private epoch;
|
|
103
|
+
/** When the last read after a charge started, to space them out. */
|
|
104
|
+
private lastSpendReadAt;
|
|
83
105
|
/**
|
|
84
106
|
* @param options - origin, credential chain, the plugin's sign-in and the route's model state.
|
|
85
107
|
*/
|
|
86
108
|
constructor(options: AccountServiceOptions);
|
|
109
|
+
private timeout;
|
|
110
|
+
private cancelTimeout;
|
|
111
|
+
/** Something the pages show changed: answer every waiting `?since=` request. */
|
|
112
|
+
changed(): void;
|
|
113
|
+
/** The account changed hands (sign-in, sign-out, switch): forget the balance and tell the pages. */
|
|
114
|
+
credentialChanged(): void;
|
|
115
|
+
/**
|
|
116
|
+
* A request that may have cost money ended (a chat reply, a generation, a
|
|
117
|
+
* transcription): read the balance again shortly, and once more later if the
|
|
118
|
+
* charge had not settled yet. Repeated calls in a burst share the reads.
|
|
119
|
+
*/
|
|
120
|
+
spent(): void;
|
|
121
|
+
/** Read the balance of the current credential now (the pages hear about a change through the long poll). */
|
|
122
|
+
private readNow;
|
|
123
|
+
/**
|
|
124
|
+
* Wait until the view changes past `since`, or a while.
|
|
125
|
+
* @param since - the revision the page shows.
|
|
126
|
+
* @param signal - the page went away.
|
|
127
|
+
* @returns once there is something new, or the wait ran out.
|
|
128
|
+
*/
|
|
129
|
+
wait(since: number, signal?: AbortSignal): Promise<void>;
|
|
87
130
|
/** Links to the gateway's pages. */
|
|
88
131
|
links(): AccountView['links'];
|
|
89
132
|
/**
|
|
@@ -92,8 +135,12 @@ export declare class AccountService {
|
|
|
92
135
|
* @returns the view.
|
|
93
136
|
*/
|
|
94
137
|
view(fresh?: boolean): Promise<AccountView>;
|
|
95
|
-
/**
|
|
138
|
+
/**
|
|
139
|
+
* `GET /v1/account/auth/me` for one token. A read younger than the TTL is
|
|
140
|
+
* reused; an older one is answered at once while a new read runs behind it.
|
|
141
|
+
*/
|
|
96
142
|
private me;
|
|
143
|
+
private read;
|
|
97
144
|
/**
|
|
98
145
|
* Start the plugin's own sign-in.
|
|
99
146
|
* @param open - open the system browser (false: the page opens the link itself).
|
|
@@ -111,6 +158,8 @@ export declare class AccountService {
|
|
|
111
158
|
* @returns false when the credential is the app's account, which is signed out in the app.
|
|
112
159
|
*/
|
|
113
160
|
signOut(): Promise<boolean>;
|
|
161
|
+
/** Stop the pending reads after a charge (the plugin is going away). */
|
|
162
|
+
dispose(): void;
|
|
114
163
|
}
|
|
115
164
|
/**
|
|
116
165
|
* The account routes.
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Audio generation requests — music, podcasts and speech — checked against what
|
|
3
|
+
* the chosen model declares in the catalog's `audio` block, and turned into the
|
|
4
|
+
* body of `POST /v1/audio/generations`.
|
|
5
|
+
*
|
|
6
|
+
* Each kind sends only its own fields:
|
|
7
|
+
*
|
|
8
|
+
* | kind | wire fields |
|
|
9
|
+
* | ------- | --------------------------------------------------------------- |
|
|
10
|
+
* | music | model, prompt, lyrics, genre, mood, gender |
|
|
11
|
+
* | podcast | model, prompt, source_url |
|
|
12
|
+
* | speech | as declared; Seed: model, text_prompt, references, audio_config, watermark |
|
|
13
|
+
*
|
|
14
|
+
* The main text goes in the model's `prompt_field`. A field the model does not
|
|
15
|
+
* declare in `request_fields` is refused before anything is uploaded or charged;
|
|
16
|
+
* music and podcasts never take speech references, output formats, sample rates
|
|
17
|
+
* or a duration. Speech references, formats and sample rates come from the
|
|
18
|
+
* catalog only (count, size, length, mutual exclusions, the sample rates of
|
|
19
|
+
* each format), never from a fixed list.
|
|
20
|
+
* @module dsh-vibedev/audio/request
|
|
21
|
+
*/
|
|
22
|
+
import type { AudioCapabilities, AudioGenerationKind, MediaModel } from '../gateway/catalog.js';
|
|
23
|
+
import type { AudioQuote } from '../pricing.js';
|
|
24
|
+
import type { MediaCall, MediaRuntime } from '../runtime.js';
|
|
25
|
+
import type { TaskRecord } from '../tasks/store.js';
|
|
26
|
+
/** The kinds of audio a generation request makes. */
|
|
27
|
+
export type AudioMakeKind = Exclude<AudioGenerationKind, 'transcription'>;
|
|
28
|
+
export declare const AUDIO_MAKE_KINDS: readonly AudioMakeKind[];
|
|
29
|
+
/** What to generate. Unset fields are not sent. */
|
|
30
|
+
export interface AudioRequest {
|
|
31
|
+
/** What to make; with a named model it must match the model's declared kind. */
|
|
32
|
+
readonly kind?: AudioMakeKind;
|
|
33
|
+
/** A model id or alias; otherwise the pinned or first model of the kind. */
|
|
34
|
+
readonly model?: string;
|
|
35
|
+
/** The main text: a song description, a podcast topic, or the words to speak. */
|
|
36
|
+
readonly prompt?: string;
|
|
37
|
+
/** Music. */
|
|
38
|
+
readonly lyrics?: string;
|
|
39
|
+
readonly genre?: string;
|
|
40
|
+
readonly mood?: string;
|
|
41
|
+
readonly gender?: string;
|
|
42
|
+
/** Podcast: an http(s) page for the hosts to discuss. */
|
|
43
|
+
readonly sourceUrl?: string;
|
|
44
|
+
/** Speech: reference audio (a voice to imitate) — workspace paths, links or `chat:` attachments. */
|
|
45
|
+
readonly references?: readonly string[];
|
|
46
|
+
/** Speech: an output format the model declares (`wav`, `mp3`, …). */
|
|
47
|
+
readonly format?: string;
|
|
48
|
+
/** Speech: a sample rate the model declares for that format. */
|
|
49
|
+
readonly sampleRate?: number;
|
|
50
|
+
/** Speech: whether the provider adds its watermark, when the model takes the field. */
|
|
51
|
+
readonly watermark?: boolean;
|
|
52
|
+
/** Not an audio-generation parameter; refused for music and podcasts, and for speech unless declared. */
|
|
53
|
+
readonly duration?: number;
|
|
54
|
+
/**
|
|
55
|
+
* One user operation's key: the same key always answers with the same task,
|
|
56
|
+
* so a repeated click or a retried call never creates a second one. Letters,
|
|
57
|
+
* digits, `-` and `_`, at most 80. Generated when absent.
|
|
58
|
+
*/
|
|
59
|
+
readonly operationId?: string;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* What an audio model makes, from its declaration only.
|
|
63
|
+
* @param model - the model.
|
|
64
|
+
* @returns the kind, or undefined when the catalog does not declare it.
|
|
65
|
+
*/
|
|
66
|
+
export declare function audioKindOf(model: MediaModel): AudioMakeKind | undefined;
|
|
67
|
+
/** The wire fields a model takes. */
|
|
68
|
+
export declare function requestFieldsOf(model: MediaModel): readonly string[];
|
|
69
|
+
/** The field the main text goes in. */
|
|
70
|
+
export declare function promptFieldOf(model: MediaModel): string | undefined;
|
|
71
|
+
/** The output format and sample rate a speech request uses, from the declared formats only. */
|
|
72
|
+
export declare function audioConfigOf(model: MediaModel, format: string | undefined, sampleRate: number | undefined): {
|
|
73
|
+
format: string;
|
|
74
|
+
sample_rate?: number;
|
|
75
|
+
} | undefined;
|
|
76
|
+
/**
|
|
77
|
+
* Check a request against the model and build the body, without references
|
|
78
|
+
* (they are uploaded first; their URLs are added by {@link prepareAudioTask}).
|
|
79
|
+
* @param model - the chosen model.
|
|
80
|
+
* @param request - what to generate.
|
|
81
|
+
* @returns the body without references, and the wire fields the request uses.
|
|
82
|
+
* @throws {@link MediaError} for a field the model does not take, a missing text, or a value outside the declared choices.
|
|
83
|
+
*/
|
|
84
|
+
export declare function audioRequestBody(model: MediaModel, request: AudioRequest): Record<string, unknown>;
|
|
85
|
+
/**
|
|
86
|
+
* Choose the model for a request: the one named (an id or an alias), else the
|
|
87
|
+
* pinned or first model of the kind. A named model must make the kind asked for.
|
|
88
|
+
*/
|
|
89
|
+
export declare function chooseAudioModel(runtime: MediaRuntime, request: Pick<AudioRequest, 'kind' | 'model'>, signal?: AbortSignal): Promise<MediaModel>;
|
|
90
|
+
/** A checked, priced audio task ready to submit. */
|
|
91
|
+
export interface PreparedAudio {
|
|
92
|
+
readonly model: MediaModel;
|
|
93
|
+
readonly kind: AudioMakeKind;
|
|
94
|
+
readonly quote: AudioQuote;
|
|
95
|
+
readonly draft: TaskRecord;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Everything before an audio submission: choose the model, check every field
|
|
99
|
+
* against its declaration, upload and measure speech references, price it,
|
|
100
|
+
* confirm the spending, and build the task under the operation's key.
|
|
101
|
+
* @param runtime - the media runtime.
|
|
102
|
+
* @param call - the tool call or host call.
|
|
103
|
+
* @param request - what to generate.
|
|
104
|
+
* @param save - where the result goes.
|
|
105
|
+
* @returns the prepared task.
|
|
106
|
+
*/
|
|
107
|
+
export declare function prepareAudioTask(runtime: MediaRuntime, call: MediaCall, request: AudioRequest, save: {
|
|
108
|
+
readonly folder: string;
|
|
109
|
+
readonly stem: string;
|
|
110
|
+
}): Promise<PreparedAudio>;
|
|
111
|
+
/** One field of an audio form, for interfaces that build their form from the catalog. */
|
|
112
|
+
export interface AudioFormField {
|
|
113
|
+
/** The {@link AudioRequest} key. */
|
|
114
|
+
readonly key: 'prompt' | 'lyrics' | 'genre' | 'mood' | 'gender' | 'sourceUrl' | 'references' | 'format' | 'sampleRate' | 'watermark';
|
|
115
|
+
/** The wire field it is sent as. */
|
|
116
|
+
readonly wire: string;
|
|
117
|
+
readonly required: boolean;
|
|
118
|
+
/** Declared choices. */
|
|
119
|
+
readonly choices?: readonly string[];
|
|
120
|
+
/** For `sampleRate`: the rates of each format. */
|
|
121
|
+
readonly sampleRates?: Readonly<Record<string, readonly number[]>>;
|
|
122
|
+
/** For `references`: the declared limits. */
|
|
123
|
+
readonly limits?: NonNullable<AudioCapabilities['references']>;
|
|
124
|
+
readonly maxChars?: number;
|
|
125
|
+
}
|
|
126
|
+
/** What a form for one audio model shows. */
|
|
127
|
+
export interface AudioForm {
|
|
128
|
+
readonly model: string;
|
|
129
|
+
readonly name: string;
|
|
130
|
+
readonly kind: AudioMakeKind;
|
|
131
|
+
readonly fields: readonly AudioFormField[];
|
|
132
|
+
readonly price: AudioQuote;
|
|
133
|
+
readonly defaultFormat?: string;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* The form an interface shows for one model: only the fields the model
|
|
137
|
+
* declares, with their declared choices and limits.
|
|
138
|
+
* @param model - the model.
|
|
139
|
+
* @returns the form, or undefined for a model that cannot generate audio.
|
|
140
|
+
*/
|
|
141
|
+
export declare function audioFormOf(model: MediaModel): AudioForm | undefined;
|
package/lib/gateway/assets.d.ts
CHANGED
|
@@ -25,8 +25,8 @@ export interface UploadedAsset {
|
|
|
25
25
|
readonly sha256: string;
|
|
26
26
|
readonly bytes: number;
|
|
27
27
|
}
|
|
28
|
-
/** Purposes the plugin uploads under. */
|
|
29
|
-
export type AssetPurpose = 'video_reference';
|
|
28
|
+
/** Purposes the plugin uploads under: references of a video request, and the reference audio of a speech request. */
|
|
29
|
+
export type AssetPurpose = 'video_reference' | 'audio_reference' | (string & {});
|
|
30
30
|
/**
|
|
31
31
|
* Read an asset answer from `uploads`, `complete` or `refresh`.
|
|
32
32
|
* @param body - the parsed answer.
|
package/lib/gateway/catalog.d.ts
CHANGED
|
@@ -8,6 +8,12 @@
|
|
|
8
8
|
* the versioned per-mode declarations (`modes_schema_version: 1`). A mode the
|
|
9
9
|
* model does not list is unsupported; an unknown schema version makes the model
|
|
10
10
|
* unusable rather than guessed at.
|
|
11
|
+
*
|
|
12
|
+
* Audio capabilities follow the `audio` block: `generation_kind` (music,
|
|
13
|
+
* podcast, speech or transcription), `prompt_field`, `request_fields`,
|
|
14
|
+
* `billing_unit` and the declared limits (references, output formats and their
|
|
15
|
+
* sample rates). An audio model that declares no kind is listed but cannot be
|
|
16
|
+
* chosen for generation: neither its name nor its id decides what it makes.
|
|
11
17
|
* @module dsh-vibedev/gateway/catalog
|
|
12
18
|
*/
|
|
13
19
|
/** What a media model produces. */
|
|
@@ -81,10 +87,16 @@ export interface VideoCapabilities {
|
|
|
81
87
|
/** References must be relayed through the gateway media library. */
|
|
82
88
|
readonly gatewayRelayRequired?: boolean;
|
|
83
89
|
}
|
|
90
|
+
/**
|
|
91
|
+
* What a price is charged per: one generation, one second of output, one
|
|
92
|
+
* second of ORIGINAL audio (speech is billed on the provider's original length,
|
|
93
|
+
* not on what plays back after processing), or one minute of input audio.
|
|
94
|
+
*/
|
|
95
|
+
export type PriceUnit = 'generation' | 'second' | 'original_second' | 'minute';
|
|
84
96
|
/** One price tier: video tiers are resolutions (`720p`), image tiers size buckets (`1K`). */
|
|
85
97
|
export interface MediaPriceTier {
|
|
86
98
|
readonly tier: string;
|
|
87
|
-
readonly unit:
|
|
99
|
+
readonly unit: PriceUnit;
|
|
88
100
|
readonly amount: number;
|
|
89
101
|
}
|
|
90
102
|
/** A model's selling price, account multiplier included. */
|
|
@@ -92,23 +104,76 @@ export interface MediaPricing {
|
|
|
92
104
|
readonly currency: string;
|
|
93
105
|
readonly tiers: readonly MediaPriceTier[];
|
|
94
106
|
readonly default?: {
|
|
95
|
-
readonly unit:
|
|
107
|
+
readonly unit: PriceUnit;
|
|
96
108
|
readonly amount: number;
|
|
97
109
|
};
|
|
98
110
|
}
|
|
111
|
+
/** What an audio model does, as the catalog declares it (`audio.generation_kind`). */
|
|
112
|
+
export declare const AUDIO_GENERATION_KINDS: readonly ["music", "podcast", "speech", "transcription"];
|
|
113
|
+
export type AudioGenerationKind = typeof AUDIO_GENERATION_KINDS[number];
|
|
114
|
+
/** How an audio model is billed (`audio.billing_unit`). */
|
|
115
|
+
export type AudioBillingUnit = 'generation' | 'original_second' | 'minute';
|
|
116
|
+
/** The reference audio a speech model takes (a voice to imitate), as the catalog declares it. */
|
|
117
|
+
export interface AudioReferenceLimits {
|
|
118
|
+
readonly min: number;
|
|
119
|
+
readonly max: number;
|
|
120
|
+
readonly maxBytes?: number;
|
|
121
|
+
readonly minSeconds?: number;
|
|
122
|
+
readonly maxSeconds?: number;
|
|
123
|
+
readonly maxTotalSeconds?: number;
|
|
124
|
+
/** Accepted MIME types; absent states no restriction. */
|
|
125
|
+
readonly mimes?: readonly string[];
|
|
126
|
+
/** Request fields that may not be sent together with references. */
|
|
127
|
+
readonly exclusiveWith?: readonly string[];
|
|
128
|
+
/** The media-library purpose references are uploaded under, when the catalog names one. */
|
|
129
|
+
readonly purpose?: string;
|
|
130
|
+
}
|
|
131
|
+
/** One output format a speech model can write, with the sample rates it takes in that format. */
|
|
132
|
+
export interface AudioOutputFormat {
|
|
133
|
+
readonly format: string;
|
|
134
|
+
readonly sampleRates: readonly number[];
|
|
135
|
+
readonly defaultSampleRate?: number;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* An audio model's declared capabilities. Nothing here is assumed: a field the
|
|
139
|
+
* catalog does not declare is absent, and a model without `generationKind`
|
|
140
|
+
* cannot be chosen for any kind of generation.
|
|
141
|
+
*/
|
|
142
|
+
export interface AudioCapabilities {
|
|
143
|
+
readonly generationKind?: AudioGenerationKind;
|
|
144
|
+
/** The request field the main text goes in (`prompt`, `text_prompt`, …). */
|
|
145
|
+
readonly promptField?: string;
|
|
146
|
+
/** The request fields the model accepts, under their wire names. */
|
|
147
|
+
readonly requestFields: readonly string[];
|
|
148
|
+
readonly billingUnit?: AudioBillingUnit;
|
|
149
|
+
readonly references?: AudioReferenceLimits;
|
|
150
|
+
readonly outputFormats?: readonly AudioOutputFormat[];
|
|
151
|
+
readonly defaultFormat?: string;
|
|
152
|
+
/** Declared choices for enumerated fields (`gender`, `genre`, …), under their wire names. */
|
|
153
|
+
readonly choices?: Readonly<Record<string, readonly string[]>>;
|
|
154
|
+
readonly maxPromptChars?: number;
|
|
155
|
+
/** Transcription input limits. */
|
|
156
|
+
readonly maxInputBytes?: number;
|
|
157
|
+
readonly maxInputSeconds?: number;
|
|
158
|
+
readonly inputMimes?: readonly string[];
|
|
159
|
+
readonly languages?: readonly string[];
|
|
160
|
+
}
|
|
99
161
|
/** One media model of the catalog. */
|
|
100
162
|
export interface MediaModel {
|
|
101
163
|
readonly id: string;
|
|
102
164
|
readonly name: string;
|
|
103
165
|
readonly description?: string;
|
|
104
166
|
readonly kind: MediaKind;
|
|
105
|
-
/**
|
|
106
|
-
readonly
|
|
167
|
+
/** Other ids the gateway answers to for this model. */
|
|
168
|
+
readonly aliases?: readonly string[];
|
|
169
|
+
/** For audio models: what they produce, from `audio.generation_kind` only; absent when undeclared. */
|
|
170
|
+
readonly audioKind?: Exclude<AudioGenerationKind, 'transcription'>;
|
|
107
171
|
/** Endpoints the catalog lists for the model. */
|
|
108
172
|
readonly endpoints: readonly string[];
|
|
109
173
|
readonly inputModalities: readonly string[];
|
|
110
174
|
readonly pricing?: MediaPricing;
|
|
111
175
|
readonly video?: VideoCapabilities;
|
|
176
|
+
readonly audio?: AudioCapabilities;
|
|
112
177
|
}
|
|
113
178
|
type Json = Record<string, unknown>;
|
|
114
179
|
/**
|
|
@@ -124,6 +189,12 @@ export declare function parseVideoCapabilities(raw: Json): VideoCapabilities;
|
|
|
124
189
|
* @returns the usable modes and their constraints.
|
|
125
190
|
*/
|
|
126
191
|
export declare function effectiveVideoModes(video: VideoCapabilities): Partial<Record<VideoMode, VideoModeConstraint>>;
|
|
192
|
+
/**
|
|
193
|
+
* Parse an audio capability block.
|
|
194
|
+
* @param raw - the block.
|
|
195
|
+
* @returns the declared capabilities; `generationKind` is absent unless declared.
|
|
196
|
+
*/
|
|
197
|
+
export declare function parseAudioCapabilities(raw: Json): AudioCapabilities;
|
|
127
198
|
/**
|
|
128
199
|
* Parse a `/v1/models` body into its media models.
|
|
129
200
|
* @param body - `{data:[...]}` or a bare array.
|
package/lib/gateway/http.d.ts
CHANGED
|
@@ -31,6 +31,11 @@ export interface GatewayHttpOptions {
|
|
|
31
31
|
readonly userAgent: string;
|
|
32
32
|
readonly fetch?: typeof globalThis.fetch;
|
|
33
33
|
readonly sleep?: (ms: number, signal?: AbortSignal) => Promise<void>;
|
|
34
|
+
/** Told about every request the gateway accepted (2xx), after the fact. */
|
|
35
|
+
readonly onAccepted?: (request: {
|
|
36
|
+
readonly method: string;
|
|
37
|
+
readonly path: string;
|
|
38
|
+
}) => void;
|
|
34
39
|
}
|
|
35
40
|
/** One request. */
|
|
36
41
|
export interface GatewayRequest {
|
package/lib/index.d.ts
CHANGED
|
@@ -24,11 +24,13 @@ import type { Context, Volatile } from '@deepseek-ai/cordis';
|
|
|
24
24
|
import Schema from '@deepseek-ai/schemastery';
|
|
25
25
|
export { MediaError } from './gateway/errors.js';
|
|
26
26
|
export { MediaHostService } from './service.js';
|
|
27
|
-
export type { HostTarget, MediaTaskView } from './service.js';
|
|
27
|
+
export type { HostTarget, MediaOutputView, MediaTaskView } from './service.js';
|
|
28
|
+
export type { AudioForm, AudioFormField, AudioMakeKind, AudioRequest } from './audio/request.js';
|
|
29
|
+
export type { AudioQuote } from './pricing.js';
|
|
28
30
|
export type { ImageRequest } from './tools/image.js';
|
|
29
31
|
export type { VideoRequest } from './tools/video.js';
|
|
30
32
|
export { parseMediaCatalog } from './gateway/catalog.js';
|
|
31
|
-
export type { MediaModel, VideoCapabilities, VideoMode } from './gateway/catalog.js';
|
|
33
|
+
export type { AudioCapabilities, AudioGenerationKind, MediaModel, PriceUnit, VideoCapabilities, VideoMode } from './gateway/catalog.js';
|
|
32
34
|
export { ACCOUNT_ROUTE_PREFIX, AccountService } from './account/index.js';
|
|
33
35
|
export type { AccountSource, AccountView } from './account/index.js';
|
|
34
36
|
export { GATEWAY_SEARCH_PROVIDER_ID as SEARCH_PROVIDER_ID } from './search/provider.js';
|
|
@@ -55,6 +57,7 @@ export interface Config {
|
|
|
55
57
|
videoModel: Volatile<string>;
|
|
56
58
|
musicModel: Volatile<string>;
|
|
57
59
|
podcastModel: Volatile<string>;
|
|
60
|
+
speechModel: Volatile<string>;
|
|
58
61
|
transcriptionModel: Volatile<string>;
|
|
59
62
|
/** Gateway origin. */
|
|
60
63
|
gatewayOrigin: string;
|
|
@@ -78,6 +81,7 @@ export declare const Config: Schema<Schemastery.ObjectS<NoInfer<{
|
|
|
78
81
|
videoModel: Schema<string, string, "volatile-defined">;
|
|
79
82
|
musicModel: Schema<string, string, "volatile-defined">;
|
|
80
83
|
podcastModel: Schema<string, string, "volatile-defined">;
|
|
84
|
+
speechModel: Schema<string, string, "volatile-defined">;
|
|
81
85
|
transcriptionModel: Schema<string, string, "volatile-defined">;
|
|
82
86
|
gatewayOrigin: Schema<string, string, "defined">;
|
|
83
87
|
client: Schema<string, string, "defined">;
|
|
@@ -95,6 +99,7 @@ export declare const Config: Schema<Schemastery.ObjectS<NoInfer<{
|
|
|
95
99
|
videoModel: Schema<string, string, "volatile-defined">;
|
|
96
100
|
musicModel: Schema<string, string, "volatile-defined">;
|
|
97
101
|
podcastModel: Schema<string, string, "volatile-defined">;
|
|
102
|
+
speechModel: Schema<string, string, "volatile-defined">;
|
|
98
103
|
transcriptionModel: Schema<string, string, "volatile-defined">;
|
|
99
104
|
gatewayOrigin: Schema<string, string, "defined">;
|
|
100
105
|
client: Schema<string, string, "defined">;
|