@lincyaw/kernel 0.0.0-stage → 1.0.0-rc.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 +202 -0
- package/README.md +32 -2
- package/package.json +41 -3
- package/src/access.ts +18 -0
- package/src/anchors.ts +58 -0
- package/src/authoring.ts +56 -0
- package/src/blocks.ts +19 -0
- package/src/checks.ts +35 -0
- package/src/component-code.ts +6 -0
- package/src/contracts/agent.ts +76 -0
- package/src/contracts/anchors.ts +46 -0
- package/src/contracts/authoring-actions.ts +60 -0
- package/src/contracts/authoring-clients.ts +213 -0
- package/src/contracts/authoring.ts +180 -0
- package/src/contracts/checks.ts +29 -0
- package/src/contracts/classroom.ts +49 -0
- package/src/contracts/diagnostics.ts +37 -0
- package/src/contracts/document-audio.ts +150 -0
- package/src/contracts/document-comments.ts +31 -0
- package/src/contracts/document-history.ts +63 -0
- package/src/contracts/document-image.ts +32 -0
- package/src/contracts/document-reads.ts +57 -0
- package/src/contracts/document-voice.ts +44 -0
- package/src/contracts/document.ts +240 -0
- package/src/contracts/execution.ts +118 -0
- package/src/contracts/locale.ts +3 -0
- package/src/contracts/offline.ts +2 -0
- package/src/contracts/operations.ts +8 -0
- package/src/contracts/plugin.ts +183 -0
- package/src/contracts/presenter-panels.ts +17 -0
- package/src/contracts/preview.ts +386 -0
- package/src/contracts/production-assets.ts +61 -0
- package/src/contracts/production.ts +109 -0
- package/src/contracts/projects.ts +59 -0
- package/src/contracts/server.ts +108 -0
- package/src/contracts/service.ts +8 -0
- package/src/contracts/speech-settings.ts +16 -0
- package/src/contracts/speech.ts +149 -0
- package/src/contracts/steps.ts +15 -0
- package/src/contracts/transcription-adoption.ts +79 -0
- package/src/contracts/transcription.ts +112 -0
- package/src/contracts/voice.ts +97 -0
- package/src/contracts/voiceover-execution.ts +68 -0
- package/src/contracts/workspace.ts +458 -0
- package/src/contracts.ts +35 -0
- package/src/diagnostics.ts +7 -0
- package/src/document-authoring-actions.ts +47 -0
- package/src/document-caption-timings.ts +64 -0
- package/src/document-colors.ts +25 -0
- package/src/document-comments.ts +118 -0
- package/src/document-components.ts +251 -0
- package/src/document-operations.ts +132 -0
- package/src/document-pages.ts +215 -0
- package/src/document-reads.ts +81 -0
- package/src/document-schema.ts +85 -0
- package/src/document-voices.ts +153 -0
- package/src/document.ts +252 -0
- package/src/durations.ts +7 -0
- package/src/errors.ts +15 -0
- package/src/events.ts +32 -0
- package/src/execution.ts +25 -0
- package/src/fingerprint.ts +31 -0
- package/src/follow.ts +33 -0
- package/src/git.ts +12 -0
- package/src/html.ts +41 -0
- package/src/index.ts +82 -0
- package/src/ir.ts +367 -0
- package/src/jupyter-kernel.ts +9 -0
- package/src/kernel.ts +190 -0
- package/src/keyboard.ts +43 -0
- package/src/locale.ts +13 -0
- package/src/lock.ts +81 -0
- package/src/logging.ts +11 -0
- package/src/lower-myst.ts +312 -0
- package/src/node/assets.ts +23 -0
- package/src/node/authoring-actions.ts +50 -0
- package/src/node/authoring-clients.ts +14 -0
- package/src/node/document-audio.ts +22 -0
- package/src/node/document-history.ts +11 -0
- package/src/node/document-image.ts +8 -0
- package/src/node/document-reads.ts +10 -0
- package/src/node/document-voice.ts +7 -0
- package/src/node/ffmpeg.ts +29 -0
- package/src/node/file-version.ts +6 -0
- package/src/node/fonts.ts +22 -0
- package/src/node/index.ts +29 -0
- package/src/node/installed-packages.ts +11 -0
- package/src/node/lines.ts +24 -0
- package/src/node/locale.ts +7 -0
- package/src/node/network.ts +11 -0
- package/src/node/operator-speech.ts +91 -0
- package/src/node/packages.ts +74 -0
- package/src/node/paths.ts +7 -0
- package/src/node/projects.ts +7 -0
- package/src/node/resources.ts +86 -0
- package/src/node/revision.ts +6 -0
- package/src/node/run-directory.ts +70 -0
- package/src/node/strip-types.mjs +15 -0
- package/src/node/transcription-adoption.ts +9 -0
- package/src/node/transcription.ts +10 -0
- package/src/node/voiceover-routes.ts +73 -0
- package/src/node/voiceover.ts +199 -0
- package/src/page-source.ts +359 -0
- package/src/parameters.ts +81 -0
- package/src/preview-edit.ts +18 -0
- package/src/preview-events.ts +57 -0
- package/src/preview-frame.ts +1220 -0
- package/src/props.ts +17 -0
- package/src/published.ts +11 -0
- package/src/push-stream.ts +67 -0
- package/src/reading.ts +96 -0
- package/src/recording.ts +38 -0
- package/src/renderer-components.ts +30 -0
- package/src/requests.ts +28 -0
- package/src/responses.ts +49 -0
- package/src/run-outputs.ts +15 -0
- package/src/runner.py +454 -0
- package/src/services.ts +146 -0
- package/src/slides.ts +36 -0
- package/src/speech-words.ts +76 -0
- package/src/speech.ts +13 -0
- package/src/step-keys.ts +38 -0
- package/src/steps.ts +7 -0
- package/src/transcription-adoption.ts +105 -0
- package/src/uri-decoding.ts +8 -0
- package/src/urls.ts +13 -0
- package/src/views.ts +47 -0
- package/src/voice.ts +86 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import type { Logger } from './plugin.ts'
|
|
2
|
+
import { defineService } from './service.ts'
|
|
3
|
+
import type { ServiceToken } from './service.ts'
|
|
4
|
+
|
|
5
|
+
export type MemberRole = 'reader' | 'editor' | 'owner'
|
|
6
|
+
|
|
7
|
+
export interface Member {
|
|
8
|
+
id: string
|
|
9
|
+
name: string
|
|
10
|
+
email?: string
|
|
11
|
+
roles: MemberRole[]
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface RequestContext {
|
|
15
|
+
member: Member
|
|
16
|
+
document: string
|
|
17
|
+
rest: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type RouteHandler = (request: Request, ctx: RequestContext) => Response | Promise<Response>
|
|
21
|
+
|
|
22
|
+
export interface Route {
|
|
23
|
+
role?: MemberRole
|
|
24
|
+
maxBody?: number
|
|
25
|
+
handle: RouteHandler
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const MAX_BODY = 1 << 20
|
|
29
|
+
|
|
30
|
+
export interface ProxySpec {
|
|
31
|
+
path: string
|
|
32
|
+
target: string
|
|
33
|
+
headers?: Record<string, string>
|
|
34
|
+
role?: MemberRole
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface ServerContribution {
|
|
38
|
+
routes?: Record<string, Route>
|
|
39
|
+
proxies?: ProxySpec[]
|
|
40
|
+
close?(): void | Promise<void>
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface ServerContext {
|
|
44
|
+
deckDir: string
|
|
45
|
+
workspaceRoot: string
|
|
46
|
+
config: Record<string, unknown>
|
|
47
|
+
settings: Record<string, unknown>
|
|
48
|
+
lang?: string
|
|
49
|
+
service<T>(token: ServiceToken<T>): T | undefined
|
|
50
|
+
secret(name: string): string | undefined
|
|
51
|
+
operatorDir?: string
|
|
52
|
+
home?: boolean
|
|
53
|
+
log: Logger
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface NodeServiceContext {
|
|
57
|
+
deckDir: string
|
|
58
|
+
workspaceRoot: string
|
|
59
|
+
config: Record<string, unknown>
|
|
60
|
+
settings: Record<string, unknown>
|
|
61
|
+
lang?: string
|
|
62
|
+
secret(name: string): string | undefined
|
|
63
|
+
operatorDir?: string
|
|
64
|
+
service?<T>(token: ServiceToken<T>): T | undefined
|
|
65
|
+
log: Logger
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export type ApprovalKind = 'operate' | 'spend' | 'write' | 'agent'
|
|
69
|
+
|
|
70
|
+
export type ApprovalScope = 'content' | 'workspace'
|
|
71
|
+
|
|
72
|
+
export interface ApprovalRequest {
|
|
73
|
+
kind: ApprovalKind
|
|
74
|
+
scope?: ApprovalScope
|
|
75
|
+
title: string
|
|
76
|
+
detail?: string
|
|
77
|
+
from: string
|
|
78
|
+
member?: string
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface Approvals {
|
|
82
|
+
request(request: ApprovalRequest, signal?: AbortSignal): Promise<boolean>
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export const APPROVALS = defineService<Approvals>('presmium.approvals')
|
|
86
|
+
|
|
87
|
+
export type AgentMode = 'author'
|
|
88
|
+
|
|
89
|
+
export interface McpAccess {
|
|
90
|
+
url: string
|
|
91
|
+
token: string
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export interface McpGrant extends McpAccess {
|
|
95
|
+
revoke(): void
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** @experimental */
|
|
99
|
+
export interface McpIssueOptions {
|
|
100
|
+
confirmed?: boolean
|
|
101
|
+
document?: string
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export interface McpEndpoint {
|
|
105
|
+
issue(member: Member, mode: AgentMode, options?: McpIssueOptions): McpGrant
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export const MCP_ENDPOINT = defineService<McpEndpoint>('presmium.mcpEndpoint')
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** Operator configuration is separate from document voice choices. Secret values are write-only. */
|
|
2
|
+
export interface SpeechSettingsAnswer {
|
|
3
|
+
provider: 'volcengine'
|
|
4
|
+
model: 'seed-tts-2.0'
|
|
5
|
+
revision: string
|
|
6
|
+
configured: boolean
|
|
7
|
+
appIdConfigured?: boolean
|
|
8
|
+
source: 'saved' | 'environment' | 'none'
|
|
9
|
+
stored: boolean
|
|
10
|
+
writable: boolean
|
|
11
|
+
verification: 'configuration-only'
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type SpeechSettingsChange =
|
|
15
|
+
| { action: 'save', revision: string, apiKey?: string, appId?: string }
|
|
16
|
+
| { action: 'clear', revision: string }
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { defineService } from './service.ts'
|
|
2
|
+
|
|
3
|
+
export interface SpeechWord { text: string, start: number, end: number }
|
|
4
|
+
|
|
5
|
+
export interface Synthesis {
|
|
6
|
+
audio: Blob | string
|
|
7
|
+
mime: string
|
|
8
|
+
duration: number
|
|
9
|
+
words?: SpeechWord[]
|
|
10
|
+
key: string
|
|
11
|
+
engine: string
|
|
12
|
+
model: string
|
|
13
|
+
voice: string
|
|
14
|
+
provider: string
|
|
15
|
+
synthetic: true
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface AigcLabel {
|
|
19
|
+
Label: '1'
|
|
20
|
+
ContentProducer: string
|
|
21
|
+
ProduceID: string
|
|
22
|
+
ReservedCode1: string
|
|
23
|
+
ContentPropagator: string
|
|
24
|
+
PropagateID: string
|
|
25
|
+
ReservedCode2: string
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface TranscriptSegment { start: number, end: number, text: string, words: SpeechWord[] }
|
|
29
|
+
|
|
30
|
+
export interface Transcript {
|
|
31
|
+
lang: string
|
|
32
|
+
duration: number
|
|
33
|
+
text: string
|
|
34
|
+
segments: TranscriptSegment[]
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface SpeechEngineInfo {
|
|
38
|
+
id: string
|
|
39
|
+
provider: string
|
|
40
|
+
stt: boolean
|
|
41
|
+
tts: boolean
|
|
42
|
+
wordTimes: boolean
|
|
43
|
+
local: boolean
|
|
44
|
+
/** The engine can train a cloned voice from a sample (`SpeechService.cloneVoice`); absent means it cannot. */
|
|
45
|
+
clone?: boolean
|
|
46
|
+
/** Absent means the provider has not declared a supported authoring range. */
|
|
47
|
+
parameters?: { rate?: { min: number, max: number, step?: number }, styles?: string[] }
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface SpeechVoice {
|
|
51
|
+
engine?: string
|
|
52
|
+
voice: string
|
|
53
|
+
rate?: number
|
|
54
|
+
style?: string
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface VoiceInfo {
|
|
58
|
+
id: string
|
|
59
|
+
label: string
|
|
60
|
+
lang: string[]
|
|
61
|
+
cloned?: boolean
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** A provider list may be empty. An unsupported catalogue must not be presented as an empty list. */
|
|
65
|
+
export type VoiceCatalog =
|
|
66
|
+
| { status: 'available', source: 'provider' | 'bundled', voices: VoiceInfo[] }
|
|
67
|
+
| { status: 'unavailable', reason: 'not-configured' | 'unsupported' | 'unknown', voices: VoiceInfo[] }
|
|
68
|
+
|
|
69
|
+
export interface SynthesisRequest {
|
|
70
|
+
engine?: string
|
|
71
|
+
voice: string
|
|
72
|
+
text: string
|
|
73
|
+
lang: string
|
|
74
|
+
rate?: number
|
|
75
|
+
style?: string
|
|
76
|
+
lexicon?: Record<string, string>
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Training a cloned voice. The speaker's consent comes with the request and is registered before anything is sent:
|
|
81
|
+
* `statement` is their recording of the consent statement, kept by the operator, whose sha256 the record holds.
|
|
82
|
+
*/
|
|
83
|
+
export interface VoiceCloneRequest {
|
|
84
|
+
engine?: string
|
|
85
|
+
/** The voice to train: a slot the operator owns at the provider (Volcengine's S_…) or a new name, per the engine. */
|
|
86
|
+
voice: string
|
|
87
|
+
/** One speaker, mono, about 10–30 s. */
|
|
88
|
+
sample: Blob
|
|
89
|
+
lang?: string
|
|
90
|
+
/** What the sample says, when known: providers that compare it reject a mismatch. */
|
|
91
|
+
text?: string
|
|
92
|
+
consent: { speaker: string, scope: string, statement: Blob }
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export type VoiceCloneState = 'training' | 'ready' | 'failed' | 'unknown'
|
|
96
|
+
|
|
97
|
+
export interface VoiceCloneStatus {
|
|
98
|
+
engine: string
|
|
99
|
+
voice: string
|
|
100
|
+
state: VoiceCloneState
|
|
101
|
+
/** How many more trainings the provider allows for this voice. */
|
|
102
|
+
trainingsLeft?: number
|
|
103
|
+
message?: string
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export interface TranscriptionRequest {
|
|
107
|
+
engine?: string
|
|
108
|
+
audio: Blob
|
|
109
|
+
lang?: string
|
|
110
|
+
hints?: string[]
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export interface SpeechService {
|
|
114
|
+
engines(): Promise<SpeechEngineInfo[]>
|
|
115
|
+
/** The actual default in this document's scoped service; absent means its identity is unknown. */
|
|
116
|
+
defaultSynthesisEngine?(): Promise<string | undefined>
|
|
117
|
+
voices(engine: string, lang?: string): Promise<VoiceInfo[]>
|
|
118
|
+
/** Lists registered voices without synthesis. Absence means catalogue availability is unknown. */
|
|
119
|
+
voiceCatalog?(engine?: string, lang?: string, signal?: AbortSignal): Promise<VoiceCatalog>
|
|
120
|
+
synthesize(request: SynthesisRequest, signal?: AbortSignal): Promise<Synthesis>
|
|
121
|
+
/** Exact request/configuration lookup with verified audio bytes. Offline reuse requires an unambiguous lock. A miss never calls synthesis or transcription providers. */
|
|
122
|
+
lookup?(request: SynthesisRequest, signal?: AbortSignal): Promise<Synthesis | undefined>
|
|
123
|
+
/** Verifies the current configuration profile, or an explicit offline lock, and the actual audio SHA. */
|
|
124
|
+
cached?(key: string, signal?: AbortSignal): Promise<Synthesis | undefined>
|
|
125
|
+
alignWords?(key: string, options: { lang: string, transcribe?: boolean }, signal?: AbortSignal): Promise<SpeechWord[] | undefined>
|
|
126
|
+
pruneLock?(keep: ReadonlySet<string>): Promise<number>
|
|
127
|
+
transcribe(request: TranscriptionRequest, signal?: AbortSignal): Promise<Transcript>
|
|
128
|
+
/** Registers the consent, then starts training; only for the document's owner. Absent: the service cannot clone. */
|
|
129
|
+
cloneVoice?(request: VoiceCloneRequest, signal?: AbortSignal): Promise<VoiceCloneStatus>
|
|
130
|
+
cloneStatus?(engine: string, voice: string, signal?: AbortSignal): Promise<VoiceCloneStatus>
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export const SPEECH = defineService<SpeechService>('presmium.speech')
|
|
134
|
+
|
|
135
|
+
/** Stable author-facing reasons; provider payloads and credential values are never UI text. */
|
|
136
|
+
export function speechFailureCode(error: unknown): string | undefined {
|
|
137
|
+
const message = error instanceof Error ? error.message : typeof error === 'string' ? error : ''
|
|
138
|
+
if (/^speech-(authentication|permission|quota|busy|network|empty-audio|invalid-request|provider|cache-failed)$/.test(message)) return message
|
|
139
|
+
if (/quota exceeded.*concurrency|too many requests|answered 429|is busy: more requests/i.test(message)) return 'speech-busy'
|
|
140
|
+
if (/quota exceeded|insufficient.*(balance|credit)|payment required|balance.*insufficient/i.test(message)) return 'speech-quota'
|
|
141
|
+
if (/answered 401|45000010|unauthorized|invalid (?:(?:x-)?api.?key|token)|authentication failed/i.test(message)) return 'speech-authentication'
|
|
142
|
+
if (/EACCES|ENOSPC|EROFS|speech.*cache.*(write|failed)/i.test(message)) return 'speech-cache-failed'
|
|
143
|
+
if (/answered 403|resource.*mismatch|permission denied|access denied|service.*not.*(enabled|activated)/i.test(message)) return 'speech-permission'
|
|
144
|
+
if (/fetch failed|ECONNREFUSED|ENOTFOUND|ETIMEDOUT|ECONNRESET|UND_ERR|network error/i.test(message)) return 'speech-network'
|
|
145
|
+
if (/gave no audio|no audio data/i.test(message)) return 'speech-empty-audio'
|
|
146
|
+
if (/answered 4\d\d|45000001|4500200[01]|invalid argument/i.test(message)) return 'speech-invalid-request'
|
|
147
|
+
if (/answered 5\d\d|55000000/i.test(message)) return 'speech-provider'
|
|
148
|
+
return undefined
|
|
149
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { CanonicalSource } from '../ir.ts'
|
|
2
|
+
import type { DocumentChangeAnswer } from './workspace.ts'
|
|
3
|
+
import type { NarrationNode, TeachingDocument } from './document.ts'
|
|
4
|
+
import type { Member } from './server.ts'
|
|
5
|
+
|
|
6
|
+
export type TranscriptionAdoptionPart = { type: 'text', text: string } | { type: 'preserve', key: string }
|
|
7
|
+
export interface TranscriptionAdoptionTarget {
|
|
8
|
+
sectionId: string
|
|
9
|
+
stepId: string
|
|
10
|
+
narrationId?: string
|
|
11
|
+
segmentIds: string[]
|
|
12
|
+
content?: TranscriptionAdoptionPart[]
|
|
13
|
+
discardAtoms?: string[]
|
|
14
|
+
shared?: 'edit-shared' | 'fork'
|
|
15
|
+
affectedStepIds?: string[]
|
|
16
|
+
}
|
|
17
|
+
export interface TranscriptionAdoptionDraft {
|
|
18
|
+
id: string
|
|
19
|
+
source: CanonicalSource
|
|
20
|
+
candidate: { id: string, revision: string }
|
|
21
|
+
draftRevision: number
|
|
22
|
+
targets: TranscriptionAdoptionTarget[]
|
|
23
|
+
/** Explicitly confirmed cleanup, only for cues actually removed by this adoption. */
|
|
24
|
+
removeCueTimings?: string[]
|
|
25
|
+
}
|
|
26
|
+
export interface TranscriptionAdoptionApply {
|
|
27
|
+
planId: string
|
|
28
|
+
source: CanonicalSource
|
|
29
|
+
candidate: { id: string, revision: string }
|
|
30
|
+
draftRevision: number
|
|
31
|
+
}
|
|
32
|
+
export interface TranscriptionAdoptionConflict { code: string, target?: number, key?: string, stepIds?: string[] }
|
|
33
|
+
export interface TranscriptionAdoptionPreview {
|
|
34
|
+
targets: { sectionId: string, stepId: string, narrationId?: string, outputNarrationId: string, segmentIds: string[], sourceText: string, before: NarrationNode[], proposed: NarrationNode[], atoms: { key: string, node: Exclude<NarrationNode, { type: 'text' }> }[] }[]
|
|
35
|
+
conflicts: TranscriptionAdoptionConflict[]
|
|
36
|
+
impact: { narrationIds: string[], stepIds: string[], sharedStepIds: string[], forks: { from: string, to: string, stepId: string, cues: { from: string, to: string }[] }[], removedAtoms: string[], removedCueTimings: string[] }
|
|
37
|
+
}
|
|
38
|
+
export interface TranscriptionAdoptionPlan extends TranscriptionAdoptionPreview {
|
|
39
|
+
document: string
|
|
40
|
+
changeId: string
|
|
41
|
+
source: CanonicalSource
|
|
42
|
+
candidate: { id: string, revision: string }
|
|
43
|
+
draftRevision: number
|
|
44
|
+
planId?: string
|
|
45
|
+
expiresAt?: string
|
|
46
|
+
content?: TeachingDocument
|
|
47
|
+
}
|
|
48
|
+
export interface TranscriptionAdoptionAnswer extends DocumentChangeAnswer {
|
|
49
|
+
adoption: { planId: string, candidate: { id: string, revision: string }, draftRevision: number }
|
|
50
|
+
}
|
|
51
|
+
/** Agent-safe surface. An editor HTTP adapter receives its trusted write path separately. */
|
|
52
|
+
export interface DocumentTranscriptionAdoption {
|
|
53
|
+
plan(document: string, draft: TranscriptionAdoptionDraft, member: Member): Promise<TranscriptionAdoptionPlan>
|
|
54
|
+
/** The adoption's checks without writing: plan, draft revision, candidate, current source; `apply` checks everything again. */
|
|
55
|
+
check(document: string, request: TranscriptionAdoptionApply, member: Member): Promise<void>
|
|
56
|
+
apply(document: string, request: TranscriptionAdoptionApply, member: Member): Promise<TranscriptionAdoptionAnswer>
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const id = { type: 'string', minLength: 1, maxLength: 200, pattern: '^[^\u0000-\u001f]+$', not: { enum: ['__proto__', 'constructor', 'prototype'] } }
|
|
60
|
+
const digest = { type: 'string', pattern: '^[a-f0-9]{64}$' }
|
|
61
|
+
const key = { type: 'string', minLength: 1, maxLength: 512 }
|
|
62
|
+
const source = { type: 'object', properties: { documentId: id, version: { type: 'string', pattern: '^[a-f0-9]{20}$' }, fingerprint: { type: 'string', pattern: '^[a-f0-9]{16}$' } }, required: ['documentId', 'version', 'fingerprint'], additionalProperties: false }
|
|
63
|
+
const candidate = { type: 'object', properties: { id: digest, revision: digest }, required: ['id', 'revision'], additionalProperties: false }
|
|
64
|
+
const draftRevision = { type: 'integer', minimum: 0, maximum: Number.MAX_SAFE_INTEGER }
|
|
65
|
+
const unique = (items: object) => ({ type: 'array', items, uniqueItems: true, maxItems: 1000 })
|
|
66
|
+
export const TRANSCRIPTION_ADOPTION_DRAFT_SCHEMA = {
|
|
67
|
+
type: 'object', additionalProperties: false, required: ['id', 'source', 'candidate', 'draftRevision', 'targets'],
|
|
68
|
+
properties: { id, source, candidate, draftRevision, removeCueTimings: unique(id), targets: { type: 'array', minItems: 1, maxItems: 100, items: {
|
|
69
|
+
type: 'object', additionalProperties: false, required: ['sectionId', 'stepId', 'segmentIds'], properties: {
|
|
70
|
+
sectionId: id, stepId: id, narrationId: id, segmentIds: { ...unique(id), minItems: 1 },
|
|
71
|
+
shared: { enum: ['edit-shared', 'fork'] }, affectedStepIds: unique(id), discardAtoms: unique(key),
|
|
72
|
+
content: { type: 'array', maxItems: 1000, items: { oneOf: [
|
|
73
|
+
{ type: 'object', properties: { type: { const: 'text' }, text: { type: 'string', maxLength: 1_048_576 } }, required: ['type', 'text'], additionalProperties: false },
|
|
74
|
+
{ type: 'object', properties: { type: { const: 'preserve' }, key }, required: ['type', 'key'], additionalProperties: false },
|
|
75
|
+
] } },
|
|
76
|
+
},
|
|
77
|
+
} } },
|
|
78
|
+
}
|
|
79
|
+
export const TRANSCRIPTION_ADOPTION_APPLY_SCHEMA = { type: 'object', additionalProperties: false, properties: { planId: id, source, candidate, draftRevision }, required: ['planId', 'source', 'candidate', 'draftRevision'] }
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import type { CanonicalSource } from '../ir.ts'
|
|
2
|
+
import type { TimeRange } from './document.ts'
|
|
3
|
+
import type { Member } from './server.ts'
|
|
4
|
+
import type { SpeechEngineInfo } from './speech.ts'
|
|
5
|
+
import type { Finding } from './checks.ts'
|
|
6
|
+
|
|
7
|
+
export const TRANSCRIPTION_REQUEST_MAX_BYTES = 32 << 10
|
|
8
|
+
|
|
9
|
+
export interface TranscriptionCapabilities {
|
|
10
|
+
source: CanonicalSource
|
|
11
|
+
available: boolean
|
|
12
|
+
status: 'available' | 'unavailable' | 'unknown'
|
|
13
|
+
reason?: 'speech-unavailable' | 'engine-unavailable' | 'audio-unavailable' | 'services-unavailable'
|
|
14
|
+
/** Provider declaration and local configuration are known; authentication/recognition is not verified. */
|
|
15
|
+
verification: 'configuration-only'
|
|
16
|
+
findings?: Finding[]
|
|
17
|
+
engines: SpeechEngineInfo[]
|
|
18
|
+
range: 'whole-asset'
|
|
19
|
+
limits: { audioBytes: number, durationMs: number, tasks: number, requestBytes: number }
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** A confirmed recognition request; no script, clip or voice selection is changed. */
|
|
23
|
+
export interface DocumentTranscriptionRequest {
|
|
24
|
+
intent: string
|
|
25
|
+
source: CanonicalSource
|
|
26
|
+
assetId: string
|
|
27
|
+
audioSha256: string
|
|
28
|
+
engine: string
|
|
29
|
+
lang?: string
|
|
30
|
+
hints?: string[]
|
|
31
|
+
confirmed: true
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Agent input excludes host confirmation; HTTP and Node validate the confirmed form below. */
|
|
35
|
+
export const DOCUMENT_TRANSCRIPTION_INPUT_SCHEMA = {
|
|
36
|
+
type: 'object', additionalProperties: false,
|
|
37
|
+
properties: {
|
|
38
|
+
intent: { type: 'string', pattern: '^[A-Za-z\\d_-]{16,128}$' },
|
|
39
|
+
source: { type: 'object', additionalProperties: false, properties: { documentId: { type: 'string', minLength: 1, maxLength: 200 }, version: { type: 'string', pattern: '^[a-f\\d]{20}$' }, fingerprint: { type: 'string', pattern: '^[a-f\\d]{16}$' } }, required: ['documentId', 'version', 'fingerprint'] },
|
|
40
|
+
assetId: { type: 'string', minLength: 1, maxLength: 200 },
|
|
41
|
+
audioSha256: { type: 'string', pattern: '^[a-f\\d]{64}$' },
|
|
42
|
+
engine: { type: 'string', minLength: 1, maxLength: 128 },
|
|
43
|
+
lang: { type: 'string', minLength: 1, maxLength: 64 },
|
|
44
|
+
hints: { type: 'array', maxItems: 100, items: { type: 'string', maxLength: 200 } },
|
|
45
|
+
},
|
|
46
|
+
required: ['intent', 'source', 'assetId', 'audioSha256', 'engine'],
|
|
47
|
+
} as const
|
|
48
|
+
|
|
49
|
+
export const DOCUMENT_TRANSCRIPTION_REQUEST_SCHEMA = {
|
|
50
|
+
...DOCUMENT_TRANSCRIPTION_INPUT_SCHEMA,
|
|
51
|
+
properties: { ...DOCUMENT_TRANSCRIPTION_INPUT_SCHEMA.properties, confirmed: { const: true } },
|
|
52
|
+
required: [...DOCUMENT_TRANSCRIPTION_INPUT_SCHEMA.required, 'confirmed'],
|
|
53
|
+
} as const
|
|
54
|
+
|
|
55
|
+
export interface TranscriptionRevision {
|
|
56
|
+
transcriptionId: string
|
|
57
|
+
revision: string
|
|
58
|
+
createdAt: string
|
|
59
|
+
source: CanonicalSource
|
|
60
|
+
audio: { assetId: string, sha256: string, sourceRange: TimeRange }
|
|
61
|
+
request: { engine: string, provider: string, local: boolean, lang?: string, hints: string[], fingerprint: string }
|
|
62
|
+
result: {
|
|
63
|
+
lang: string
|
|
64
|
+
text: string
|
|
65
|
+
segments: { id: string, text: string, startMs?: number, endMs?: number, words?: { text: string, startMs: number, endMs: number }[] }[]
|
|
66
|
+
}
|
|
67
|
+
timing: 'words' | 'segments' | 'none'
|
|
68
|
+
warnings: string[]
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export type TranscriptionTaskState = 'queued' | 'running' | 'cancelling' | 'cancelled' | 'done' | 'failed'
|
|
72
|
+
export interface TranscriptionTask {
|
|
73
|
+
kind: 'transcription'
|
|
74
|
+
id: string
|
|
75
|
+
intent: string
|
|
76
|
+
document: string
|
|
77
|
+
source: CanonicalSource
|
|
78
|
+
audio: { assetId: string, sha256: string }
|
|
79
|
+
sequence: number
|
|
80
|
+
state: TranscriptionTaskState
|
|
81
|
+
current: boolean
|
|
82
|
+
staleReason?: 'document-changed' | 'document-unavailable' | 'audio-changed'
|
|
83
|
+
progress: { phase: 'queued' | 'recognizing' | 'saving' | 'finished' }
|
|
84
|
+
createdAt: string
|
|
85
|
+
startedAt?: string
|
|
86
|
+
finishedAt?: string
|
|
87
|
+
result?: { transcriptionId: string, revision: string }
|
|
88
|
+
error?: string
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface TranscriptionCancellation { intent: string, state: TranscriptionTaskState, task?: TranscriptionTask }
|
|
92
|
+
export interface TranscriptionListAnswer {
|
|
93
|
+
tasks: TranscriptionTask[]
|
|
94
|
+
issues: { id: string, code: string }[]
|
|
95
|
+
truncated?: true
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Workspace-owned authoring capability, shared by HTTP and agent adapters. */
|
|
99
|
+
export interface DocumentTranscriptions {
|
|
100
|
+
capabilities(document: string, member: Member): Promise<TranscriptionCapabilities>
|
|
101
|
+
start(document: string, request: DocumentTranscriptionRequest, member: Member, signal?: AbortSignal): Promise<TranscriptionTask>
|
|
102
|
+
list(document: string, member: Member): Promise<TranscriptionListAnswer>
|
|
103
|
+
get(document: string, query: { id: string } | { intent: string }, member: Member): Promise<TranscriptionTask>
|
|
104
|
+
readResult(document: string, id: string, revision: string, member: Member): Promise<TranscriptionRevision>
|
|
105
|
+
/** Rechecks current canonical source and the exact original audio, without recognizing it again. */
|
|
106
|
+
verifyResult(document: string, id: string, revision: string, source: CanonicalSource, member: Member): Promise<TranscriptionRevision>
|
|
107
|
+
cancel(document: string, query: { id: string } | { intent: string }, member: Member): Promise<TranscriptionCancellation>
|
|
108
|
+
/** Checks a start with the same path, without storing a task or calling the provider, so an agent asks only for one that would run. */
|
|
109
|
+
checkStart(document: string, request: DocumentTranscriptionRequest, member: Member, signal?: AbortSignal): Promise<void>
|
|
110
|
+
/** Undefined when there is something to cancel; otherwise the unchanged state that cancel would report. */
|
|
111
|
+
checkCancel(document: string, query: { id: string } | { intent: string }, member: Member): Promise<TranscriptionCancellation | undefined>
|
|
112
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type { SpeechEngineInfo, SpeechVoice, VoiceCatalog } from './speech.ts'
|
|
2
|
+
import type { CanonicalSource } from '../ir.ts'
|
|
3
|
+
import { defineService } from './service.ts'
|
|
4
|
+
|
|
5
|
+
/** A step number is usable only with the structure binding returned by the current snapshot. */
|
|
6
|
+
export interface VoiceTarget { section: string, step: number, binding: string }
|
|
7
|
+
export type VoiceScope = { kind: 'document' } | { kind: 'page', section: string } | { kind: 'step', target: VoiceTarget } | { kind: 'steps', targets: VoiceTarget[] }
|
|
8
|
+
export interface StepVoiceOverride extends VoiceTarget { voice: SpeechVoice }
|
|
9
|
+
export interface VoiceOverrides { pages: Record<string, SpeechVoice>, steps: StepVoiceOverride[] }
|
|
10
|
+
export interface ResolvedVoice {
|
|
11
|
+
target: VoiceTarget
|
|
12
|
+
voice?: SpeechVoice
|
|
13
|
+
inheritedFrom: 'step' | 'page' | 'document' | 'unset'
|
|
14
|
+
override?: SpeechVoice
|
|
15
|
+
editable: boolean
|
|
16
|
+
reason?: 'cue-step' | 'step-binding-stale' | 'recording-unavailable'
|
|
17
|
+
}
|
|
18
|
+
/** Registered by the actual lecture plugin implementation, never inferred from authored configuration. */
|
|
19
|
+
export const VOICEOVER = defineService<{ version: 1, preview: true }>('presmium.voiceover')
|
|
20
|
+
export interface VoiceoverCapability {
|
|
21
|
+
available: boolean
|
|
22
|
+
reason?: 'not-configured' | 'voice-unset' | 'step-binding-stale' | 'speech-unavailable' | 'voice-unavailable' | 'catalog-unavailable'
|
|
23
|
+
providers: SpeechEngineInfo[]
|
|
24
|
+
defaultEngine?: string
|
|
25
|
+
}
|
|
26
|
+
export interface VoiceoverStepStatus {
|
|
27
|
+
target: VoiceTarget
|
|
28
|
+
stepId?: string
|
|
29
|
+
state: 'ready' | 'missing' | 'blocked' | 'empty'
|
|
30
|
+
utterances: number
|
|
31
|
+
cached: number
|
|
32
|
+
reason?: string
|
|
33
|
+
}
|
|
34
|
+
export interface VoiceoverSnapshot {
|
|
35
|
+
revision: string
|
|
36
|
+
source: CanonicalSource
|
|
37
|
+
capability: VoiceoverCapability
|
|
38
|
+
steps: VoiceoverStepStatus[]
|
|
39
|
+
jobs: VoiceoverJob[]
|
|
40
|
+
/** Real speech units whose source cannot yet be assigned to one full-document step. */
|
|
41
|
+
unboundByPage?: { section: string, state: VoiceoverStepStatus['state'], utterances: number, cached: number, reason?: string }[]
|
|
42
|
+
}
|
|
43
|
+
export type VoiceoverJobState = 'queued' | 'running' | 'cancelling' | 'cancelled' | 'done' | 'failed' | 'stale'
|
|
44
|
+
export interface VoiceoverJob {
|
|
45
|
+
id: string
|
|
46
|
+
sequence: number
|
|
47
|
+
/** Client-owned generation intent, available before the job response is delivered. */
|
|
48
|
+
intent: string
|
|
49
|
+
revision: string
|
|
50
|
+
source: CanonicalSource
|
|
51
|
+
scope: VoiceScope
|
|
52
|
+
state: VoiceoverJobState
|
|
53
|
+
total: number
|
|
54
|
+
completed: number
|
|
55
|
+
failed: number
|
|
56
|
+
createdAt: string
|
|
57
|
+
startedAt?: string
|
|
58
|
+
finishedAt?: string
|
|
59
|
+
error?: string
|
|
60
|
+
}
|
|
61
|
+
export interface VoiceoverRequest { intent: string, revision: string, source: CanonicalSource, scope: VoiceScope, confirmed: boolean }
|
|
62
|
+
/** Cancellation can precede admission; a pre-admission refusal is not a fabricated job. */
|
|
63
|
+
export interface VoiceoverCancellation { intent: string, state: VoiceoverJobState, job?: VoiceoverJob }
|
|
64
|
+
export interface VoicePreviewRequest {
|
|
65
|
+
revision: string
|
|
66
|
+
source: CanonicalSource
|
|
67
|
+
voice: SpeechVoice
|
|
68
|
+
mode: 'sample' | 'step'
|
|
69
|
+
target?: VoiceTarget
|
|
70
|
+
confirmed: boolean
|
|
71
|
+
quote: string
|
|
72
|
+
}
|
|
73
|
+
export type VoicePreviewQuery = Omit<VoicePreviewRequest, 'confirmed' | 'quote' | 'source'>
|
|
74
|
+
export interface VoicePreviewQuote extends VoicePreviewQuery {
|
|
75
|
+
source: CanonicalSource
|
|
76
|
+
text: string
|
|
77
|
+
provider: string
|
|
78
|
+
local: boolean
|
|
79
|
+
cached: boolean
|
|
80
|
+
quote: string
|
|
81
|
+
}
|
|
82
|
+
export interface VoicePreviewAnswer {
|
|
83
|
+
revision: string
|
|
84
|
+
source: CanonicalSource
|
|
85
|
+
voice: SpeechVoice
|
|
86
|
+
text: string
|
|
87
|
+
cached: boolean
|
|
88
|
+
key: string
|
|
89
|
+
audio: string
|
|
90
|
+
duration: number
|
|
91
|
+
provider: string
|
|
92
|
+
}
|
|
93
|
+
export interface VoiceCatalogAnswer {
|
|
94
|
+
engines: SpeechEngineInfo[]
|
|
95
|
+
catalog: VoiceCatalog
|
|
96
|
+
engine?: string
|
|
97
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { CanonicalSource } from '../ir.ts'
|
|
2
|
+
import type { Member } from './server.ts'
|
|
3
|
+
import type { VoiceoverCancellation, VoiceoverJob, VoiceoverRequest, VoiceoverSnapshot, VoicePreviewAnswer, VoicePreviewQuery, VoicePreviewQuote, VoicePreviewRequest } from './voice.ts'
|
|
4
|
+
import { defineService } from './service.ts'
|
|
5
|
+
|
|
6
|
+
export interface VoiceoverStatusQuery { source?: CanonicalSource, revision?: string }
|
|
7
|
+
export type VoiceoverStart = Omit<VoiceoverRequest, 'confirmed'>
|
|
8
|
+
export type VoicePreviewStart = Omit<VoicePreviewRequest, 'confirmed'>
|
|
9
|
+
export type VoicePreviewCheck = VoicePreviewQuery & { source?: CanonicalSource }
|
|
10
|
+
export type VoiceoverJobQuery = { id: string } | { intent: string }
|
|
11
|
+
export interface VoiceoverProgress { completed: number, failed: number, error?: string }
|
|
12
|
+
export interface VoiceoverRunResult extends VoiceoverProgress { state: 'done' | 'failed' | 'stale' | 'cancelled' }
|
|
13
|
+
/** A claim on the existing generation lock. Dispose after execution or failed admission. */
|
|
14
|
+
export interface PreparedVoiceover {
|
|
15
|
+
source: CanonicalSource
|
|
16
|
+
revision: string
|
|
17
|
+
scope: VoiceoverStart['scope']
|
|
18
|
+
total: number
|
|
19
|
+
run(signal: AbortSignal, progress: (value: VoiceoverProgress) => void): Promise<VoiceoverRunResult>
|
|
20
|
+
dispose(): void
|
|
21
|
+
}
|
|
22
|
+
export interface VoicePreviewMedia extends Omit<VoicePreviewAnswer, 'audio'> { bytes: Uint8Array, mediaType: string }
|
|
23
|
+
/** Stateless plugin execution. The host owns intents and jobs; discovery creates neither. */
|
|
24
|
+
export interface VoiceoverExecution {
|
|
25
|
+
status(query: VoiceoverStatusQuery, signal: AbortSignal): Promise<Omit<VoiceoverSnapshot, 'jobs'>>
|
|
26
|
+
prepare(request: VoiceoverStart, signal: AbortSignal): Promise<PreparedVoiceover>
|
|
27
|
+
quote(query: VoicePreviewCheck, signal: AbortSignal): Promise<VoicePreviewQuote>
|
|
28
|
+
preview(request: VoicePreviewStart, signal: AbortSignal): Promise<VoicePreviewMedia>
|
|
29
|
+
}
|
|
30
|
+
export const VOICEOVER_EXECUTION = defineService<VoiceoverExecution>('presmium.voiceoverExecution')
|
|
31
|
+
|
|
32
|
+
export interface VoiceoverObservation extends VoiceoverCancellation { error?: string, current?: boolean | 'unknown' }
|
|
33
|
+
export interface VoiceoverJobsAnswer { document: string, jobs: (VoiceoverJob & { current: boolean | 'unknown' })[], idempotency: 'process', limits: { intents: number, recentJobs: number } }
|
|
34
|
+
export interface VoicePreviewResource extends Omit<VoicePreviewAnswer, 'audio'> { id: string, mediaType: string, byteLength: number, sha256: string }
|
|
35
|
+
export interface VoicePreviewResourceData { resource: VoicePreviewResource, bytes: Uint8Array }
|
|
36
|
+
/** Shared author entry. Adapters obtain spending approval; model input cannot supply confirmation. */
|
|
37
|
+
export interface DocumentVoiceTasks {
|
|
38
|
+
snapshot(document: string, query: VoiceoverStatusQuery, member: Member, signal?: AbortSignal): Promise<VoiceoverSnapshot>
|
|
39
|
+
listJobs(document: string, member: Member): Promise<VoiceoverJobsAnswer>
|
|
40
|
+
getJob(document: string, query: VoiceoverJobQuery, member: Member): Promise<VoiceoverObservation>
|
|
41
|
+
start(document: string, request: VoiceoverStart, member: Member, signal?: AbortSignal): Promise<VoiceoverJob>
|
|
42
|
+
cancel(document: string, query: VoiceoverJobQuery, member: Member): Promise<VoiceoverCancellation>
|
|
43
|
+
quotePreview(document: string, query: VoicePreviewCheck, member: Member, signal?: AbortSignal): Promise<VoicePreviewQuote>
|
|
44
|
+
preview(document: string, request: VoicePreviewStart, member: Member, signal?: AbortSignal): Promise<VoicePreviewResource>
|
|
45
|
+
readPreview(document: string, id: string, member: Member): Promise<VoicePreviewResourceData>
|
|
46
|
+
}
|
|
47
|
+
export const DOCUMENT_VOICE_TASKS = defineService<DocumentVoiceTasks>('presmium.documentVoiceTasks')
|
|
48
|
+
|
|
49
|
+
const source = { type: 'object', additionalProperties: false, properties: { documentId: { type: 'string', minLength: 1, maxLength: 200 }, version: { type: 'string', pattern: '^[a-f\\d]{20}$' }, fingerprint: { type: 'string', pattern: '^[a-f\\d]{16}$' } }, required: ['documentId', 'version', 'fingerprint'] } as const
|
|
50
|
+
const revision = { type: 'string', pattern: '^[a-f\\d]{16}$' } as const
|
|
51
|
+
const target = { type: 'object', additionalProperties: false, properties: { section: { type: 'string', minLength: 1, maxLength: 200 }, step: { type: 'integer', minimum: 0 }, binding: revision }, required: ['section', 'step', 'binding'] } as const
|
|
52
|
+
const scope = { oneOf: [
|
|
53
|
+
{ type: 'object', additionalProperties: false, properties: { kind: { const: 'document' } }, required: ['kind'] },
|
|
54
|
+
{ type: 'object', additionalProperties: false, properties: { kind: { const: 'page' }, section: target.properties.section }, required: ['kind', 'section'] },
|
|
55
|
+
{ type: 'object', additionalProperties: false, properties: { kind: { const: 'step' }, target }, required: ['kind', 'target'] },
|
|
56
|
+
{ type: 'object', additionalProperties: false, properties: { kind: { const: 'steps' }, targets: { type: 'array', items: target, minItems: 1, maxItems: 500 } }, required: ['kind', 'targets'] },
|
|
57
|
+
] } as const
|
|
58
|
+
const intent = { type: 'string', pattern: '^[A-Za-z\\d_-]{16,128}$' } as const
|
|
59
|
+
export const VOICEOVER_STATUS_SCHEMA = { type: 'object', additionalProperties: false, properties: { source, revision } } as const
|
|
60
|
+
export const VOICEOVER_START_SCHEMA = { type: 'object', additionalProperties: false, properties: { intent, source, revision, scope }, required: ['intent', 'source', 'revision', 'scope'] } as const
|
|
61
|
+
export const VOICEOVER_JOB_QUERY_SCHEMA = { oneOf: [
|
|
62
|
+
{ type: 'object', additionalProperties: false, properties: { id: { type: 'string', pattern: '^[a-f\\d]{8}-[a-f\\d]{4}-[a-f\\d]{4}-[a-f\\d]{4}-[a-f\\d]{12}$' } }, required: ['id'] },
|
|
63
|
+
{ type: 'object', additionalProperties: false, properties: { intent }, required: ['intent'] },
|
|
64
|
+
] } as const
|
|
65
|
+
const voice = { type: 'object', additionalProperties: false, properties: { voice: { type: 'string', minLength: 1, maxLength: 200 }, engine: { type: 'string', minLength: 1, maxLength: 200 }, rate: { type: 'number', exclusiveMinimum: 0 }, style: { type: 'string', maxLength: 200 } }, required: ['voice'] } as const
|
|
66
|
+
const preview = { revision, source, voice, mode: { enum: ['sample', 'step'] }, target } as const
|
|
67
|
+
export const VOICE_PREVIEW_CHECK_SCHEMA = { type: 'object', additionalProperties: false, properties: preview, required: ['revision', 'voice', 'mode'], allOf: [{ if: { properties: { mode: { const: 'step' } } }, then: { required: ['target'] } }] } as const
|
|
68
|
+
export const VOICE_PREVIEW_START_SCHEMA = { type: 'object', additionalProperties: false, properties: { ...preview, quote: { type: 'string', pattern: '^[a-f\\d]{64}$' } }, required: ['source', 'revision', 'voice', 'mode', 'quote'], allOf: VOICE_PREVIEW_CHECK_SCHEMA.allOf } as const
|