@contractspec/example.voice-providers 3.7.6 → 3.7.10

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.
@@ -3,7 +3,7 @@ $ bun run prebuild && bun run build:bundle && bun run build:types
3
3
  $ contractspec-bun-build prebuild
4
4
  $ contractspec-bun-build transpile
5
5
  [contractspec-bun-build] transpile target=bun root=src entries=10 noBundle=false
6
- Bundled 10 modules in 33ms
6
+ Bundled 10 modules in 19ms
7
7
 
8
8
  ./connection.sample.js 1.50 KB (entry point)
9
9
  ./run.js 5.65 KB (entry point)
@@ -17,7 +17,7 @@ Bundled 10 modules in 33ms
17
17
  handlers/create-provider.js 1.27 KB (entry point)
18
18
 
19
19
  [contractspec-bun-build] transpile target=node root=src entries=10 noBundle=false
20
- Bundled 10 modules in 50ms
20
+ Bundled 10 modules in 21ms
21
21
 
22
22
  ./connection.sample.js 1.49 KB (entry point)
23
23
  ./run.js 5.64 KB (entry point)
@@ -31,7 +31,7 @@ Bundled 10 modules in 50ms
31
31
  handlers/create-provider.js 1.26 KB (entry point)
32
32
 
33
33
  [contractspec-bun-build] transpile target=browser root=src entries=10 noBundle=false
34
- Bundled 10 modules in 29ms
34
+ Bundled 10 modules in 28ms
35
35
 
36
36
  ./connection.sample.js 1.49 KB (entry point)
37
37
  ./run.js 5.64 KB (entry point)
package/AGENTS.md CHANGED
@@ -1,4 +1,4 @@
1
- # AI Agent Guide -- `@contractspec/example.voice-providers`
1
+ # AI Agent Guide `@contractspec/example.voice-providers`
2
2
 
3
3
  Scope: `packages/examples/voice-providers/*`
4
4
 
@@ -6,28 +6,53 @@ Voice provider example: Gradium and Fal text-to-speech integration patterns.
6
6
 
7
7
  ## Quick Context
8
8
 
9
- - **Layer**: example
10
- - **Related Packages**: `integration.providers-impls`, `lib.contracts-spec`, `lib.contracts-integrations`
11
-
12
- ## What This Demonstrates
13
-
14
- - TTS provider integration pattern with connection samples
15
- - Handler-per-action pattern (create-provider, list-voices, synthesize)
16
- - Run script for one-shot execution
17
-
18
- ## Public Exports
19
-
20
- - `.` -- root barrel
21
- - `./connection.sample` -- sample connection config
22
- - `./handlers/create-provider` -- provider creation
23
- - `./handlers/list-voices` -- voice listing
24
- - `./handlers/synthesize` -- speech synthesis
25
- - `./run` -- execution entry point
26
- - `./docs`, `./example`
9
+ - Layer: `example`.
10
+ - Package visibility: published package.
11
+ - Primary consumers are example explorers, template authors, and documentation readers.
12
+ - Related packages: `@contractspec/integration.providers-impls`, `@contractspec/lib.contracts-integrations`, `@contractspec/lib.contracts-spec`, `@contractspec/tool.bun`, `@contractspec/tool.typescript`.
13
+
14
+ ## Architecture
15
+
16
+ - `src/connection.sample.ts` is part of the package's public or composition surface.
17
+ - `src/docs/` contains docblocks and documentation-facing exports.
18
+ - `src/example.ts` is the runnable example entrypoint.
19
+ - `src/handlers/` contains handlers or demo adapters wired to contract surfaces.
20
+ - `src/index.ts` is the root public barrel and package entrypoint.
21
+ - `src/run.ts` is part of the package's public or composition surface.
22
+ - `src/voice-providers.feature.ts` defines a feature entrypoint.
23
+
24
+ ## Public Surface
25
+
26
+ - Export `.` resolves through `./src/index.ts`.
27
+ - Export `./connection.sample` resolves through `./src/connection.sample.ts`.
28
+ - Export `./docs` resolves through `./src/docs/index.ts`.
29
+ - Export `./docs/voice-providers.docblock` resolves through `./src/docs/voice-providers.docblock.ts`.
30
+ - Export `./example` resolves through `./src/example.ts`.
31
+ - Export `./handlers/create-provider` resolves through `./src/handlers/create-provider.ts`.
32
+ - Export `./handlers/list-voices` resolves through `./src/handlers/list-voices.ts`.
33
+ - Export `./handlers/synthesize` resolves through `./src/handlers/synthesize.ts`.
34
+ - Export `./run` resolves through `./src/run.ts`.
35
+ - Export `./voice-providers.feature` resolves through `./src/voice-providers.feature.ts`.
36
+
37
+ ## Guardrails
38
+
39
+ - Keep the example package demonstrative, buildable, and aligned with the exported feature surface.
40
+ - Do not add hidden production assumptions that are not actually implemented in the example.
41
+ - Changes here can affect downstream packages such as `@contractspec/integration.providers-impls`, `@contractspec/lib.contracts-integrations`, `@contractspec/lib.contracts-spec`, `@contractspec/tool.bun`, `@contractspec/tool.typescript`.
42
+ - Changes here can affect downstream packages such as `@contractspec/integration.providers-impls`, `@contractspec/lib.contracts-integrations`, `@contractspec/lib.contracts-spec`, `@contractspec/tool.bun`, `@contractspec/tool.typescript`.
27
43
 
28
44
  ## Local Commands
29
45
 
30
- - Build: `bun run build`
31
- - Dev: `bun run dev`
32
- - Test: `bun test --pass-with-no-tests`
33
- - Typecheck: `bun run typecheck`
46
+ - `bun run dev` — contractspec-bun-build dev
47
+ - `bun run build`bun run prebuild && bun run build:bundle && bun run build:types
48
+ - `bun run test`bun test --pass-with-no-tests
49
+ - `bun run lint` — bun lint:fix
50
+ - `bun run lint:check` — biome check .
51
+ - `bun run lint:fix` — biome check --write --unsafe --only=nursery/useSortedClasses . && biome check --write .
52
+ - `bun run typecheck` — tsc --noEmit
53
+ - `bun run publish:pkg` — bun publish --tolerate-republish --ignore-scripts --verbose
54
+ - `bun run publish:pkg:canary` — bun publish:pkg --tag canary
55
+ - `bun run clean` — rimraf dist .turbo
56
+ - `bun run build:bundle` — contractspec-bun-build transpile
57
+ - `bun run build:types` — contractspec-bun-build types
58
+ - `bun run prebuild` — contractspec-bun-build prebuild
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # @contractspec/example.voice-providers
2
2
 
3
+ ## 3.7.10
4
+
5
+ ### Patch Changes
6
+
7
+ - 1a44cb6: feat: improve examples to increase coverage of Contracts type
8
+ - Updated dependencies [1a44cb6]
9
+ - @contractspec/integration.providers-impls@3.8.2
10
+ - @contractspec/lib.contracts-integrations@3.8.2
11
+ - @contractspec/lib.contracts-spec@4.1.2
12
+
13
+ ## 3.7.9
14
+
15
+ ### Patch Changes
16
+
17
+ - fix: release
18
+ - Updated dependencies
19
+ - @contractspec/integration.providers-impls@3.8.1
20
+ - @contractspec/lib.contracts-integrations@3.8.1
21
+ - @contractspec/lib.contracts-spec@4.1.1
22
+
3
23
  ## 3.7.6
4
24
 
5
25
  ### Patch Changes
@@ -418,6 +438,7 @@
418
438
  - 7f3203a: fix: make workspace test runs resilient when packages have no tests
419
439
 
420
440
  Updates package test scripts to pass cleanly when no matching test files exist:
441
+
421
442
  - Uses `bun test --pass-with-no-tests` in Bun-based packages that currently ship without test files.
422
443
  - Uses `jest --passWithNoTests` for the UI kit web package.
423
444
  - Adds `.vscode-test.mjs` for `vscode-contractspec` so VS Code extension test runs have an explicit config and stop failing on missing default configuration.
package/README.md CHANGED
@@ -1,14 +1,75 @@
1
1
  # @contractspec/example.voice-providers
2
2
 
3
- Website: https://contractspec.io/
3
+ Website: https://contractspec.io
4
4
 
5
- Voice provider example for `ai-voice.gradium` and `ai-voice.fal`.
5
+ **Voice provider example: Gradium and Fal text-to-speech integration patterns.**
6
6
 
7
- This package demonstrates:
7
+ ## What This Demonstrates
8
8
 
9
- - provider selection by integration key
10
- - listing available voices
11
- - synthesizing audio from plain text
12
- - connection metadata samples for BYOK setups
9
+ - TTS provider integration pattern with connection samples.
10
+ - Handler-per-action pattern (create-provider, list-voices, synthesize).
11
+ - Run script for one-shot execution.
12
+ - `src/docs/` contains docblocks and documentation-facing exports.
13
+ - `src/handlers/` contains handlers or demo adapters wired to contract surfaces.
14
+ - `src/docs/` contains docblocks and documentation-facing exports.
13
15
 
14
- Use this as a reference for wiring voice synthesis into workflows or service handlers.
16
+ ## Running Locally
17
+
18
+ From `packages/examples/voice-providers`:
19
+ - `bun run dev`
20
+ - `bun run build`
21
+ - `bun run test`
22
+ - `bun run typecheck`
23
+
24
+ ## Usage
25
+
26
+ Use `@contractspec/example.voice-providers` as a reference implementation, or import its exported surfaces into a workspace that composes ContractSpec examples and bundles.
27
+
28
+ ## Architecture
29
+
30
+ - `src/connection.sample.ts` is part of the package's public or composition surface.
31
+ - `src/docs/` contains docblocks and documentation-facing exports.
32
+ - `src/example.ts` is the runnable example entrypoint.
33
+ - `src/handlers/` contains handlers or demo adapters wired to contract surfaces.
34
+ - `src/index.ts` is the root public barrel and package entrypoint.
35
+ - `src/run.ts` is part of the package's public or composition surface.
36
+ - `src/voice-providers.feature.ts` defines a feature entrypoint.
37
+
38
+ ## Public Entry Points
39
+
40
+ - Export `.` resolves through `./src/index.ts`.
41
+ - Export `./connection.sample` resolves through `./src/connection.sample.ts`.
42
+ - Export `./docs` resolves through `./src/docs/index.ts`.
43
+ - Export `./docs/voice-providers.docblock` resolves through `./src/docs/voice-providers.docblock.ts`.
44
+ - Export `./example` resolves through `./src/example.ts`.
45
+ - Export `./handlers/create-provider` resolves through `./src/handlers/create-provider.ts`.
46
+ - Export `./handlers/list-voices` resolves through `./src/handlers/list-voices.ts`.
47
+ - Export `./handlers/synthesize` resolves through `./src/handlers/synthesize.ts`.
48
+ - Export `./run` resolves through `./src/run.ts`.
49
+ - Export `./voice-providers.feature` resolves through `./src/voice-providers.feature.ts`.
50
+
51
+ ## Local Commands
52
+
53
+ - `bun run dev` — contractspec-bun-build dev
54
+ - `bun run build` — bun run prebuild && bun run build:bundle && bun run build:types
55
+ - `bun run test` — bun test --pass-with-no-tests
56
+ - `bun run lint` — bun lint:fix
57
+ - `bun run lint:check` — biome check .
58
+ - `bun run lint:fix` — biome check --write --unsafe --only=nursery/useSortedClasses . && biome check --write .
59
+ - `bun run typecheck` — tsc --noEmit
60
+ - `bun run publish:pkg` — bun publish --tolerate-republish --ignore-scripts --verbose
61
+ - `bun run publish:pkg:canary` — bun publish:pkg --tag canary
62
+ - `bun run clean` — rimraf dist .turbo
63
+ - `bun run build:bundle` — contractspec-bun-build transpile
64
+ - `bun run build:types` — contractspec-bun-build types
65
+ - `bun run prebuild` — contractspec-bun-build prebuild
66
+
67
+ ## Recent Updates
68
+
69
+ - Replace eslint+prettier by biomejs to optimize speed.
70
+ - Missing contract layers.
71
+ - Major change to content generation.
72
+
73
+ ## Notes
74
+
75
+ - Works alongside `@contractspec/integration.providers-impls`, `@contractspec/lib.contracts-integrations`, `@contractspec/lib.contracts-spec`, `@contractspec/tool.bun`, `@contractspec/tool.typescript`.
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
+ export * from './connection.sample';
2
+ export { default as example } from './example';
1
3
  export * from './handlers/create-provider';
2
4
  export * from './handlers/list-voices';
3
5
  export * from './handlers/synthesize';
4
- export * from './connection.sample';
5
6
  export * from './voice-providers.feature';
6
- export { default as example } from './example';
7
7
  import './docs';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contractspec/example.voice-providers",
3
- "version": "3.7.6",
3
+ "version": "3.7.10",
4
4
  "description": "Voice provider example: Gradium and Fal text-to-speech integration patterns.",
5
5
  "type": "module",
6
6
  "types": "./dist/index.d.ts",
@@ -85,21 +85,21 @@
85
85
  "dev": "contractspec-bun-build dev",
86
86
  "clean": "rimraf dist .turbo",
87
87
  "lint": "bun lint:fix",
88
- "lint:fix": "eslint src --fix",
89
- "lint:check": "eslint src",
88
+ "lint:fix": "biome check --write --unsafe --only=nursery/useSortedClasses . && biome check --write .",
89
+ "lint:check": "biome check .",
90
90
  "test": "bun test --pass-with-no-tests",
91
91
  "prebuild": "contractspec-bun-build prebuild",
92
92
  "typecheck": "tsc --noEmit"
93
93
  },
94
94
  "dependencies": {
95
- "@contractspec/integration.providers-impls": "3.7.6",
96
- "@contractspec/lib.contracts-spec": "3.7.6",
97
- "@contractspec/lib.contracts-integrations": "3.7.6"
95
+ "@contractspec/integration.providers-impls": "3.8.2",
96
+ "@contractspec/lib.contracts-spec": "4.1.2",
97
+ "@contractspec/lib.contracts-integrations": "3.8.2"
98
98
  },
99
99
  "devDependencies": {
100
- "@contractspec/tool.typescript": "3.7.6",
100
+ "@contractspec/tool.typescript": "3.7.8",
101
101
  "typescript": "^5.9.3",
102
- "@contractspec/tool.bun": "3.7.6"
102
+ "@contractspec/tool.bun": "3.7.8"
103
103
  },
104
104
  "publishConfig": {
105
105
  "access": "public",
@@ -1,54 +1,54 @@
1
1
  import type { IntegrationConnection } from '@contractspec/lib.contracts-integrations';
2
2
 
3
3
  export const gradiumVoiceConnection: IntegrationConnection = {
4
- meta: {
5
- id: 'conn-gradium-voice-demo',
6
- tenantId: 'acme-inc',
7
- integrationKey: 'ai-voice.gradium',
8
- integrationVersion: '1.0.0',
9
- label: 'Gradium Voice',
10
- environment: 'production',
11
- createdAt: '2026-01-01T00:00:00.000Z',
12
- updatedAt: '2026-01-01T00:00:00.000Z',
13
- },
14
- ownershipMode: 'byok',
15
- config: {
16
- defaultVoiceId: 'YTpq7expH9539ERJ',
17
- region: 'eu',
18
- outputFormat: 'wav',
19
- },
20
- secretProvider: 'vault',
21
- secretRef: 'vault://integrations/acme-inc/conn-gradium-voice-demo',
22
- status: 'connected',
4
+ meta: {
5
+ id: 'conn-gradium-voice-demo',
6
+ tenantId: 'acme-inc',
7
+ integrationKey: 'ai-voice.gradium',
8
+ integrationVersion: '1.0.0',
9
+ label: 'Gradium Voice',
10
+ environment: 'production',
11
+ createdAt: '2026-01-01T00:00:00.000Z',
12
+ updatedAt: '2026-01-01T00:00:00.000Z',
13
+ },
14
+ ownershipMode: 'byok',
15
+ config: {
16
+ defaultVoiceId: 'YTpq7expH9539ERJ',
17
+ region: 'eu',
18
+ outputFormat: 'wav',
19
+ },
20
+ secretProvider: 'vault',
21
+ secretRef: 'vault://integrations/acme-inc/conn-gradium-voice-demo',
22
+ status: 'connected',
23
23
  };
24
24
 
25
25
  export const falVoiceConnection: IntegrationConnection = {
26
- meta: {
27
- id: 'conn-fal-voice-demo',
28
- tenantId: 'acme-inc',
29
- integrationKey: 'ai-voice.fal',
30
- integrationVersion: '1.0.0',
31
- label: 'Fal Voice',
32
- environment: 'production',
33
- createdAt: '2026-01-01T00:00:00.000Z',
34
- updatedAt: '2026-01-01T00:00:00.000Z',
35
- },
36
- ownershipMode: 'byok',
37
- config: {
38
- modelId: 'fal-ai/chatterbox/text-to-speech',
39
- defaultVoiceUrl:
40
- 'https://storage.googleapis.com/chatterbox-demo-samples/prompts/male_rickmorty.mp3',
41
- defaultExaggeration: 0.25,
42
- defaultTemperature: 0.7,
43
- defaultCfg: 0.5,
44
- pollIntervalMs: 1000,
45
- },
46
- secretProvider: 'vault',
47
- secretRef: 'vault://integrations/acme-inc/conn-fal-voice-demo',
48
- status: 'connected',
26
+ meta: {
27
+ id: 'conn-fal-voice-demo',
28
+ tenantId: 'acme-inc',
29
+ integrationKey: 'ai-voice.fal',
30
+ integrationVersion: '1.0.0',
31
+ label: 'Fal Voice',
32
+ environment: 'production',
33
+ createdAt: '2026-01-01T00:00:00.000Z',
34
+ updatedAt: '2026-01-01T00:00:00.000Z',
35
+ },
36
+ ownershipMode: 'byok',
37
+ config: {
38
+ modelId: 'fal-ai/chatterbox/text-to-speech',
39
+ defaultVoiceUrl:
40
+ 'https://storage.googleapis.com/chatterbox-demo-samples/prompts/male_rickmorty.mp3',
41
+ defaultExaggeration: 0.25,
42
+ defaultTemperature: 0.7,
43
+ defaultCfg: 0.5,
44
+ pollIntervalMs: 1000,
45
+ },
46
+ secretProvider: 'vault',
47
+ secretRef: 'vault://integrations/acme-inc/conn-fal-voice-demo',
48
+ status: 'connected',
49
49
  };
50
50
 
51
51
  export const voiceSampleConnections: IntegrationConnection[] = [
52
- gradiumVoiceConnection,
53
- falVoiceConnection,
52
+ gradiumVoiceConnection,
53
+ falVoiceConnection,
54
54
  ];
@@ -2,47 +2,47 @@ import type { DocBlock } from '@contractspec/lib.contracts-spec/docs';
2
2
  import { registerDocBlocks } from '@contractspec/lib.contracts-spec/docs';
3
3
 
4
4
  const blocks: DocBlock[] = [
5
- {
6
- id: 'docs.examples.voice-providers',
7
- title: 'Voice Providers (example)',
8
- summary:
9
- 'Multi-provider voice integration example covering Gradium and Fal text-to-speech flows.',
10
- kind: 'reference',
11
- visibility: 'public',
12
- route: '/docs/examples/voice-providers',
13
- tags: ['voice', 'tts', 'gradium', 'fal', 'example'],
14
- body:
15
- '## What this example shows\n' +
16
- '- Provider selection for `ai-voice.gradium` and `ai-voice.fal`.\n' +
17
- '- Listing voice catalogs and synthesizing text into audio bytes.\n' +
18
- '- Connection metadata patterns for BYOK secret references.\n\n' +
19
- '## Secrets and config\n' +
20
- '- `apiKey` for each provider.\n' +
21
- '- Gradium config: `defaultVoiceId`, `region`, `outputFormat`.\n' +
22
- '- Fal config: `modelId`, `defaultVoiceUrl`, synthesis tuning fields.\n\n' +
23
- '## Guardrails\n' +
24
- '- Keep API keys in secret providers only.\n' +
25
- '- Prefer declarative provider config over hardcoded runtime options.\n' +
26
- '- Keep synthesis side effects explicit for deterministic workflows.',
27
- },
28
- {
29
- id: 'docs.examples.voice-providers.usage',
30
- title: 'Voice Providers - Usage',
31
- summary:
32
- 'How to wire provider factory and synthesis helpers in runtime code.',
33
- kind: 'usage',
34
- visibility: 'public',
35
- route: '/docs/examples/voice-providers/usage',
36
- tags: ['voice', 'usage'],
37
- body:
38
- '## Usage\n' +
39
- '- Call `createVoiceProvider` with integration key, secrets, and config.\n' +
40
- '- Use `listVoices` to expose voice choices in admin/config screens.\n' +
41
- '- Use `synthesizeVoice` for message generation or workflow steps.\n\n' +
42
- '## Notes\n' +
43
- '- Fal uses an audio URL output; this example downloads bytes for a canonical result shape.\n' +
44
- '- Gradium maps provider output formats into ContractSpec voice result conventions.',
45
- },
5
+ {
6
+ id: 'docs.examples.voice-providers',
7
+ title: 'Voice Providers (example)',
8
+ summary:
9
+ 'Multi-provider voice integration example covering Gradium and Fal text-to-speech flows.',
10
+ kind: 'reference',
11
+ visibility: 'public',
12
+ route: '/docs/examples/voice-providers',
13
+ tags: ['voice', 'tts', 'gradium', 'fal', 'example'],
14
+ body:
15
+ '## What this example shows\n' +
16
+ '- Provider selection for `ai-voice.gradium` and `ai-voice.fal`.\n' +
17
+ '- Listing voice catalogs and synthesizing text into audio bytes.\n' +
18
+ '- Connection metadata patterns for BYOK secret references.\n\n' +
19
+ '## Secrets and config\n' +
20
+ '- `apiKey` for each provider.\n' +
21
+ '- Gradium config: `defaultVoiceId`, `region`, `outputFormat`.\n' +
22
+ '- Fal config: `modelId`, `defaultVoiceUrl`, synthesis tuning fields.\n\n' +
23
+ '## Guardrails\n' +
24
+ '- Keep API keys in secret providers only.\n' +
25
+ '- Prefer declarative provider config over hardcoded runtime options.\n' +
26
+ '- Keep synthesis side effects explicit for deterministic workflows.',
27
+ },
28
+ {
29
+ id: 'docs.examples.voice-providers.usage',
30
+ title: 'Voice Providers - Usage',
31
+ summary:
32
+ 'How to wire provider factory and synthesis helpers in runtime code.',
33
+ kind: 'usage',
34
+ visibility: 'public',
35
+ route: '/docs/examples/voice-providers/usage',
36
+ tags: ['voice', 'usage'],
37
+ body:
38
+ '## Usage\n' +
39
+ '- Call `createVoiceProvider` with integration key, secrets, and config.\n' +
40
+ '- Use `listVoices` to expose voice choices in admin/config screens.\n' +
41
+ '- Use `synthesizeVoice` for message generation or workflow steps.\n\n' +
42
+ '## Notes\n' +
43
+ '- Fal uses an audio URL output; this example downloads bytes for a canonical result shape.\n' +
44
+ '- Gradium maps provider output formats into ContractSpec voice result conventions.',
45
+ },
46
46
  ];
47
47
 
48
48
  registerDocBlocks(blocks);
package/src/example.ts CHANGED
@@ -1,32 +1,32 @@
1
1
  import { defineExample } from '@contractspec/lib.contracts-spec';
2
2
 
3
3
  const example = defineExample({
4
- meta: {
5
- key: 'voice-providers',
6
- version: '1.0.0',
7
- title: 'Voice Providers (Gradium and Fal)',
8
- description:
9
- 'Multi-provider voice integration example for Gradium and Fal text-to-speech adapters.',
10
- kind: 'integration',
11
- visibility: 'public',
12
- stability: 'experimental',
13
- owners: ['@platform.integrations'],
14
- tags: ['voice', 'tts', 'gradium', 'fal', 'integrations'],
15
- },
16
- docs: {
17
- rootDocId: 'docs.examples.voice-providers',
18
- usageDocId: 'docs.examples.voice-providers.usage',
19
- },
20
- entrypoints: {
21
- packageName: '@contractspec/example.voice-providers',
22
- docs: './docs',
23
- },
24
- surfaces: {
25
- templates: true,
26
- sandbox: { enabled: true, modes: ['markdown', 'specs'] },
27
- studio: { enabled: true, installable: true },
28
- mcp: { enabled: true },
29
- },
4
+ meta: {
5
+ key: 'voice-providers',
6
+ version: '1.0.0',
7
+ title: 'Voice Providers (Gradium and Fal)',
8
+ description:
9
+ 'Multi-provider voice integration example for Gradium and Fal text-to-speech adapters.',
10
+ kind: 'integration',
11
+ visibility: 'public',
12
+ stability: 'experimental',
13
+ owners: ['@platform.integrations'],
14
+ tags: ['voice', 'tts', 'gradium', 'fal', 'integrations'],
15
+ },
16
+ docs: {
17
+ rootDocId: 'docs.examples.voice-providers',
18
+ usageDocId: 'docs.examples.voice-providers.usage',
19
+ },
20
+ entrypoints: {
21
+ packageName: '@contractspec/example.voice-providers',
22
+ docs: './docs',
23
+ },
24
+ surfaces: {
25
+ templates: true,
26
+ sandbox: { enabled: true, modes: ['markdown', 'specs'] },
27
+ studio: { enabled: true, installable: true },
28
+ mcp: { enabled: true },
29
+ },
30
30
  });
31
31
 
32
32
  export default example;
@@ -5,66 +5,66 @@ import type { TTSProvider } from '@contractspec/lib.contracts-integrations';
5
5
  export type VoiceIntegrationKey = 'ai-voice.gradium' | 'ai-voice.fal';
6
6
 
7
7
  export interface VoiceProviderSecrets {
8
- apiKey: string;
8
+ apiKey: string;
9
9
  }
10
10
 
11
11
  export interface VoiceProviderConfig {
12
- defaultVoiceId?: string;
13
- region?: 'eu' | 'us';
14
- baseUrl?: string;
15
- timeoutMs?: number;
16
- outputFormat?:
17
- | 'wav'
18
- | 'pcm'
19
- | 'opus'
20
- | 'ulaw_8000'
21
- | 'alaw_8000'
22
- | 'pcm_16000'
23
- | 'pcm_24000';
24
- modelId?: string;
25
- defaultVoiceUrl?: string;
26
- defaultExaggeration?: number;
27
- defaultTemperature?: number;
28
- defaultCfg?: number;
29
- pollIntervalMs?: number;
12
+ defaultVoiceId?: string;
13
+ region?: 'eu' | 'us';
14
+ baseUrl?: string;
15
+ timeoutMs?: number;
16
+ outputFormat?:
17
+ | 'wav'
18
+ | 'pcm'
19
+ | 'opus'
20
+ | 'ulaw_8000'
21
+ | 'alaw_8000'
22
+ | 'pcm_16000'
23
+ | 'pcm_24000';
24
+ modelId?: string;
25
+ defaultVoiceUrl?: string;
26
+ defaultExaggeration?: number;
27
+ defaultTemperature?: number;
28
+ defaultCfg?: number;
29
+ pollIntervalMs?: number;
30
30
  }
31
31
 
32
32
  export interface VoiceProviderFactoryInput {
33
- integrationKey: VoiceIntegrationKey;
34
- secrets: VoiceProviderSecrets;
35
- config?: VoiceProviderConfig;
33
+ integrationKey: VoiceIntegrationKey;
34
+ secrets: VoiceProviderSecrets;
35
+ config?: VoiceProviderConfig;
36
36
  }
37
37
 
38
38
  export function createVoiceProvider(
39
- input: VoiceProviderFactoryInput
39
+ input: VoiceProviderFactoryInput
40
40
  ): TTSProvider {
41
- const { integrationKey, secrets, config } = input;
41
+ const { integrationKey, secrets, config } = input;
42
42
 
43
- if (!secrets.apiKey) {
44
- throw new Error('Voice provider apiKey is required.');
45
- }
43
+ if (!secrets.apiKey) {
44
+ throw new Error('Voice provider apiKey is required.');
45
+ }
46
46
 
47
- switch (integrationKey) {
48
- case 'ai-voice.gradium':
49
- return new GradiumVoiceProvider({
50
- apiKey: secrets.apiKey,
51
- defaultVoiceId: config?.defaultVoiceId,
52
- region: config?.region,
53
- baseUrl: config?.baseUrl,
54
- timeoutMs: config?.timeoutMs,
55
- outputFormat: config?.outputFormat,
56
- });
57
- case 'ai-voice.fal':
58
- return new FalVoiceProvider({
59
- apiKey: secrets.apiKey,
60
- modelId: config?.modelId,
61
- defaultVoiceUrl: config?.defaultVoiceUrl,
62
- defaultExaggeration: config?.defaultExaggeration,
63
- defaultTemperature: config?.defaultTemperature,
64
- defaultCfg: config?.defaultCfg,
65
- pollIntervalMs: config?.pollIntervalMs,
66
- });
67
- default:
68
- throw new Error(`Unsupported voice provider: ${integrationKey}`);
69
- }
47
+ switch (integrationKey) {
48
+ case 'ai-voice.gradium':
49
+ return new GradiumVoiceProvider({
50
+ apiKey: secrets.apiKey,
51
+ defaultVoiceId: config?.defaultVoiceId,
52
+ region: config?.region,
53
+ baseUrl: config?.baseUrl,
54
+ timeoutMs: config?.timeoutMs,
55
+ outputFormat: config?.outputFormat,
56
+ });
57
+ case 'ai-voice.fal':
58
+ return new FalVoiceProvider({
59
+ apiKey: secrets.apiKey,
60
+ modelId: config?.modelId,
61
+ defaultVoiceUrl: config?.defaultVoiceUrl,
62
+ defaultExaggeration: config?.defaultExaggeration,
63
+ defaultTemperature: config?.defaultTemperature,
64
+ defaultCfg: config?.defaultCfg,
65
+ pollIntervalMs: config?.pollIntervalMs,
66
+ });
67
+ default:
68
+ throw new Error(`Unsupported voice provider: ${integrationKey}`);
69
+ }
70
70
  }
@@ -1,13 +1,13 @@
1
1
  import type { Voice } from '@contractspec/lib.contracts-integrations';
2
2
 
3
3
  import {
4
- createVoiceProvider,
5
- type VoiceProviderFactoryInput,
4
+ createVoiceProvider,
5
+ type VoiceProviderFactoryInput,
6
6
  } from './create-provider';
7
7
 
8
8
  export async function listVoices(
9
- input: VoiceProviderFactoryInput
9
+ input: VoiceProviderFactoryInput
10
10
  ): Promise<Voice[]> {
11
- const provider = createVoiceProvider(input);
12
- return provider.listVoices();
11
+ const provider = createVoiceProvider(input);
12
+ return provider.listVoices();
13
13
  }
@@ -1,20 +1,20 @@
1
1
  import type {
2
- TTSSynthesisInput,
3
- TTSSynthesisResult,
2
+ TTSSynthesisInput,
3
+ TTSSynthesisResult,
4
4
  } from '@contractspec/lib.contracts-integrations';
5
5
 
6
6
  import {
7
- createVoiceProvider,
8
- type VoiceProviderFactoryInput,
7
+ createVoiceProvider,
8
+ type VoiceProviderFactoryInput,
9
9
  } from './create-provider';
10
10
 
11
11
  export interface SynthesizeVoiceInput extends VoiceProviderFactoryInput {
12
- synthesis: TTSSynthesisInput;
12
+ synthesis: TTSSynthesisInput;
13
13
  }
14
14
 
15
15
  export async function synthesizeVoice(
16
- input: SynthesizeVoiceInput
16
+ input: SynthesizeVoiceInput
17
17
  ): Promise<TTSSynthesisResult> {
18
- const provider = createVoiceProvider(input);
19
- return provider.synthesize(input.synthesis);
18
+ const provider = createVoiceProvider(input);
19
+ return provider.synthesize(input.synthesis);
20
20
  }
package/src/index.ts CHANGED
@@ -1,7 +1,7 @@
1
+ export * from './connection.sample';
2
+ export { default as example } from './example';
1
3
  export * from './handlers/create-provider';
2
4
  export * from './handlers/list-voices';
3
5
  export * from './handlers/synthesize';
4
- export * from './connection.sample';
5
6
  export * from './voice-providers.feature';
6
- export { default as example } from './example';
7
7
  import './docs';
package/src/run.ts CHANGED
@@ -1,180 +1,180 @@
1
- import { listVoices } from './handlers/list-voices';
2
- import { synthesizeVoice } from './handlers/synthesize';
3
1
  import type {
4
- VoiceIntegrationKey,
5
- VoiceProviderConfig,
6
- VoiceProviderFactoryInput,
2
+ VoiceIntegrationKey,
3
+ VoiceProviderConfig,
4
+ VoiceProviderFactoryInput,
7
5
  } from './handlers/create-provider';
6
+ import { listVoices } from './handlers/list-voices';
7
+ import { synthesizeVoice } from './handlers/synthesize';
8
8
 
9
9
  type VoiceMode = 'list' | 'synthesize' | 'both';
10
10
 
11
11
  export async function runVoiceProvidersExampleFromEnv() {
12
- const integrationKey = resolveIntegrationKey();
13
- const mode = resolveMode();
14
- const dryRun = process.env.CONTRACTSPEC_VOICE_DRY_RUN === 'true';
15
- const config = resolveConfig(integrationKey);
16
- const text =
17
- process.env.CONTRACTSPEC_VOICE_TEXT ??
18
- 'Hello from ContractSpec voice providers example.';
19
- const voiceId = process.env.CONTRACTSPEC_VOICE_ID;
20
-
21
- if (dryRun) {
22
- return {
23
- integrationKey,
24
- mode,
25
- dryRun,
26
- text,
27
- voiceId,
28
- config,
29
- };
30
- }
31
-
32
- const input: VoiceProviderFactoryInput = {
33
- integrationKey,
34
- secrets: {
35
- apiKey: resolveApiKey(integrationKey),
36
- },
37
- config,
38
- };
39
-
40
- const output: Record<string, unknown> = {
41
- integrationKey,
42
- mode,
43
- dryRun,
44
- };
45
-
46
- if (mode === 'list' || mode === 'both') {
47
- const voices = await listVoices(input);
48
- output.voices = voices;
49
- }
50
-
51
- if (mode === 'synthesize' || mode === 'both') {
52
- const result = await synthesizeVoice({
53
- ...input,
54
- synthesis: {
55
- text,
56
- voiceId: voiceId ?? 'default',
57
- },
58
- });
59
- output.synthesis = {
60
- format: result.audio.format,
61
- sampleRateHz: result.audio.sampleRateHz,
62
- bytes: result.audio.data.length,
63
- durationMs: result.audio.durationMs,
64
- };
65
- }
66
-
67
- return output;
12
+ const integrationKey = resolveIntegrationKey();
13
+ const mode = resolveMode();
14
+ const dryRun = process.env.CONTRACTSPEC_VOICE_DRY_RUN === 'true';
15
+ const config = resolveConfig(integrationKey);
16
+ const text =
17
+ process.env.CONTRACTSPEC_VOICE_TEXT ??
18
+ 'Hello from ContractSpec voice providers example.';
19
+ const voiceId = process.env.CONTRACTSPEC_VOICE_ID;
20
+
21
+ if (dryRun) {
22
+ return {
23
+ integrationKey,
24
+ mode,
25
+ dryRun,
26
+ text,
27
+ voiceId,
28
+ config,
29
+ };
30
+ }
31
+
32
+ const input: VoiceProviderFactoryInput = {
33
+ integrationKey,
34
+ secrets: {
35
+ apiKey: resolveApiKey(integrationKey),
36
+ },
37
+ config,
38
+ };
39
+
40
+ const output: Record<string, unknown> = {
41
+ integrationKey,
42
+ mode,
43
+ dryRun,
44
+ };
45
+
46
+ if (mode === 'list' || mode === 'both') {
47
+ const voices = await listVoices(input);
48
+ output.voices = voices;
49
+ }
50
+
51
+ if (mode === 'synthesize' || mode === 'both') {
52
+ const result = await synthesizeVoice({
53
+ ...input,
54
+ synthesis: {
55
+ text,
56
+ voiceId: voiceId ?? 'default',
57
+ },
58
+ });
59
+ output.synthesis = {
60
+ format: result.audio.format,
61
+ sampleRateHz: result.audio.sampleRateHz,
62
+ bytes: result.audio.data.length,
63
+ durationMs: result.audio.durationMs,
64
+ };
65
+ }
66
+
67
+ return output;
68
68
  }
69
69
 
70
70
  function resolveMode(): VoiceMode {
71
- const raw = (process.env.CONTRACTSPEC_VOICE_MODE ?? 'both').toLowerCase();
72
- if (raw === 'list' || raw === 'synthesize' || raw === 'both') {
73
- return raw;
74
- }
75
- throw new Error(
76
- `Unsupported CONTRACTSPEC_VOICE_MODE: ${raw}. Use list, synthesize, or both.`
77
- );
71
+ const raw = (process.env.CONTRACTSPEC_VOICE_MODE ?? 'both').toLowerCase();
72
+ if (raw === 'list' || raw === 'synthesize' || raw === 'both') {
73
+ return raw;
74
+ }
75
+ throw new Error(
76
+ `Unsupported CONTRACTSPEC_VOICE_MODE: ${raw}. Use list, synthesize, or both.`
77
+ );
78
78
  }
79
79
 
80
80
  function resolveIntegrationKey(): VoiceIntegrationKey {
81
- const raw = (
82
- process.env.CONTRACTSPEC_VOICE_PROVIDER ?? 'gradium'
83
- ).toLowerCase();
84
- if (raw === 'gradium') return 'ai-voice.gradium';
85
- if (raw === 'fal') return 'ai-voice.fal';
86
- throw new Error(
87
- `Unsupported CONTRACTSPEC_VOICE_PROVIDER: ${raw}. Use gradium or fal.`
88
- );
81
+ const raw = (
82
+ process.env.CONTRACTSPEC_VOICE_PROVIDER ?? 'gradium'
83
+ ).toLowerCase();
84
+ if (raw === 'gradium') return 'ai-voice.gradium';
85
+ if (raw === 'fal') return 'ai-voice.fal';
86
+ throw new Error(
87
+ `Unsupported CONTRACTSPEC_VOICE_PROVIDER: ${raw}. Use gradium or fal.`
88
+ );
89
89
  }
90
90
 
91
91
  function resolveApiKey(integrationKey: VoiceIntegrationKey): string {
92
- const shared = process.env.CONTRACTSPEC_VOICE_API_KEY;
93
- if (shared) return shared;
94
-
95
- const specific =
96
- integrationKey === 'ai-voice.gradium'
97
- ? process.env.GRADIUM_API_KEY
98
- : process.env.FAL_KEY;
99
-
100
- if (!specific) {
101
- const envName =
102
- integrationKey === 'ai-voice.gradium' ? 'GRADIUM_API_KEY' : 'FAL_KEY';
103
- throw new Error(
104
- `Missing API key. Set CONTRACTSPEC_VOICE_API_KEY or ${envName}.`
105
- );
106
- }
107
-
108
- return specific;
92
+ const shared = process.env.CONTRACTSPEC_VOICE_API_KEY;
93
+ if (shared) return shared;
94
+
95
+ const specific =
96
+ integrationKey === 'ai-voice.gradium'
97
+ ? process.env.GRADIUM_API_KEY
98
+ : process.env.FAL_KEY;
99
+
100
+ if (!specific) {
101
+ const envName =
102
+ integrationKey === 'ai-voice.gradium' ? 'GRADIUM_API_KEY' : 'FAL_KEY';
103
+ throw new Error(
104
+ `Missing API key. Set CONTRACTSPEC_VOICE_API_KEY or ${envName}.`
105
+ );
106
+ }
107
+
108
+ return specific;
109
109
  }
110
110
 
111
111
  function resolveConfig(
112
- integrationKey: VoiceIntegrationKey
112
+ integrationKey: VoiceIntegrationKey
113
113
  ): VoiceProviderConfig {
114
- if (integrationKey === 'ai-voice.gradium') {
115
- const config: VoiceProviderConfig = {
116
- defaultVoiceId: process.env.GRADIUM_DEFAULT_VOICE_ID,
117
- region:
118
- process.env.GRADIUM_REGION === 'eu' ||
119
- process.env.GRADIUM_REGION === 'us'
120
- ? process.env.GRADIUM_REGION
121
- : undefined,
122
- baseUrl: process.env.GRADIUM_BASE_URL,
123
- timeoutMs: parseOptionalInt(process.env.GRADIUM_TIMEOUT_MS),
124
- outputFormat: parseGradiumOutputFormat(process.env.GRADIUM_OUTPUT_FORMAT),
125
- };
126
- return config;
127
- }
128
-
129
- return {
130
- modelId: process.env.FAL_MODEL_ID,
131
- defaultVoiceUrl: process.env.FAL_DEFAULT_VOICE_URL,
132
- defaultExaggeration: parseOptionalNumber(
133
- process.env.FAL_DEFAULT_EXAGGERATION
134
- ),
135
- defaultTemperature: parseOptionalNumber(
136
- process.env.FAL_DEFAULT_TEMPERATURE
137
- ),
138
- defaultCfg: parseOptionalNumber(process.env.FAL_DEFAULT_CFG),
139
- pollIntervalMs: parseOptionalInt(process.env.FAL_POLL_INTERVAL_MS),
140
- };
114
+ if (integrationKey === 'ai-voice.gradium') {
115
+ const config: VoiceProviderConfig = {
116
+ defaultVoiceId: process.env.GRADIUM_DEFAULT_VOICE_ID,
117
+ region:
118
+ process.env.GRADIUM_REGION === 'eu' ||
119
+ process.env.GRADIUM_REGION === 'us'
120
+ ? process.env.GRADIUM_REGION
121
+ : undefined,
122
+ baseUrl: process.env.GRADIUM_BASE_URL,
123
+ timeoutMs: parseOptionalInt(process.env.GRADIUM_TIMEOUT_MS),
124
+ outputFormat: parseGradiumOutputFormat(process.env.GRADIUM_OUTPUT_FORMAT),
125
+ };
126
+ return config;
127
+ }
128
+
129
+ return {
130
+ modelId: process.env.FAL_MODEL_ID,
131
+ defaultVoiceUrl: process.env.FAL_DEFAULT_VOICE_URL,
132
+ defaultExaggeration: parseOptionalNumber(
133
+ process.env.FAL_DEFAULT_EXAGGERATION
134
+ ),
135
+ defaultTemperature: parseOptionalNumber(
136
+ process.env.FAL_DEFAULT_TEMPERATURE
137
+ ),
138
+ defaultCfg: parseOptionalNumber(process.env.FAL_DEFAULT_CFG),
139
+ pollIntervalMs: parseOptionalInt(process.env.FAL_POLL_INTERVAL_MS),
140
+ };
141
141
  }
142
142
 
143
143
  function parseOptionalInt(value: string | undefined): number | undefined {
144
- if (!value) return undefined;
145
- const parsed = Number.parseInt(value, 10);
146
- return Number.isFinite(parsed) ? parsed : undefined;
144
+ if (!value) return undefined;
145
+ const parsed = Number.parseInt(value, 10);
146
+ return Number.isFinite(parsed) ? parsed : undefined;
147
147
  }
148
148
 
149
149
  function parseOptionalNumber(value: string | undefined): number | undefined {
150
- if (!value) return undefined;
151
- const parsed = Number.parseFloat(value);
152
- return Number.isFinite(parsed) ? parsed : undefined;
150
+ if (!value) return undefined;
151
+ const parsed = Number.parseFloat(value);
152
+ return Number.isFinite(parsed) ? parsed : undefined;
153
153
  }
154
154
 
155
155
  function parseGradiumOutputFormat(
156
- value: string | undefined
156
+ value: string | undefined
157
157
  ): VoiceProviderConfig['outputFormat'] {
158
- if (!value) return undefined;
159
- switch (value) {
160
- case 'wav':
161
- case 'pcm':
162
- case 'opus':
163
- case 'ulaw_8000':
164
- case 'alaw_8000':
165
- case 'pcm_16000':
166
- case 'pcm_24000':
167
- return value;
168
- default:
169
- return undefined;
170
- }
158
+ if (!value) return undefined;
159
+ switch (value) {
160
+ case 'wav':
161
+ case 'pcm':
162
+ case 'opus':
163
+ case 'ulaw_8000':
164
+ case 'alaw_8000':
165
+ case 'pcm_16000':
166
+ case 'pcm_24000':
167
+ return value;
168
+ default:
169
+ return undefined;
170
+ }
171
171
  }
172
172
 
173
173
  runVoiceProvidersExampleFromEnv()
174
- .then((result) => {
175
- console.log(JSON.stringify(result, null, 2));
176
- })
177
- .catch((error) => {
178
- console.error(error);
179
- process.exitCode = 1;
180
- });
174
+ .then((result) => {
175
+ console.log(JSON.stringify(result, null, 2));
176
+ })
177
+ .catch((error) => {
178
+ console.error(error);
179
+ process.exitCode = 1;
180
+ });
@@ -1,22 +1,22 @@
1
1
  import { defineFeature } from '@contractspec/lib.contracts-spec';
2
2
 
3
3
  export const VoiceProvidersFeature = defineFeature({
4
- meta: {
5
- key: 'voice-providers',
6
- version: '1.0.0',
7
- title: 'Voice Providers',
8
- description:
9
- 'Voice provider integration for TTS synthesis with multiple providers',
10
- domain: 'integration',
11
- owners: ['@examples'],
12
- tags: ['voice', 'tts', 'providers', 'synthesis'],
13
- stability: 'experimental',
14
- },
4
+ meta: {
5
+ key: 'voice-providers',
6
+ version: '1.0.0',
7
+ title: 'Voice Providers',
8
+ description:
9
+ 'Voice provider integration for TTS synthesis with multiple providers',
10
+ domain: 'integration',
11
+ owners: ['@examples'],
12
+ tags: ['voice', 'tts', 'providers', 'synthesis'],
13
+ stability: 'experimental',
14
+ },
15
15
 
16
- integrations: [{ key: 'voice-providers.integration.tts', version: '1.0.0' }],
16
+ integrations: [{ key: 'voice-providers.integration.tts', version: '1.0.0' }],
17
17
 
18
- docs: [
19
- 'docs.examples.voice-providers',
20
- 'docs.examples.voice-providers.usage',
21
- ],
18
+ docs: [
19
+ 'docs.examples.voice-providers',
20
+ 'docs.examples.voice-providers.usage',
21
+ ],
22
22
  });
package/tsconfig.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
- "extends": "@contractspec/tool.typescript/react-library.json",
3
- "include": ["src"],
4
- "exclude": ["node_modules", "dist"],
5
- "compilerOptions": {
6
- "rootDir": "src",
7
- "outDir": "dist"
8
- }
2
+ "extends": "@contractspec/tool.typescript/react-library.json",
3
+ "include": ["src"],
4
+ "exclude": ["node_modules", "dist"],
5
+ "compilerOptions": {
6
+ "rootDir": "src",
7
+ "outDir": "dist"
8
+ }
9
9
  }
package/tsdown.config.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { defineConfig, moduleLibrary } from '@contractspec/tool.bun';
2
2
 
3
3
  export default defineConfig(() => ({
4
- ...moduleLibrary,
4
+ ...moduleLibrary,
5
5
  }));