deepctl-core 0.2.12__tar.gz → 0.2.14__tar.gz
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.
- {deepctl_core-0.2.12 → deepctl_core-0.2.14}/PKG-INFO +2 -2
- {deepctl_core-0.2.12 → deepctl_core-0.2.14}/pyproject.toml +2 -2
- {deepctl_core-0.2.12 → deepctl_core-0.2.14}/src/deepctl_core/auth.py +8 -1
- {deepctl_core-0.2.12 → deepctl_core-0.2.14}/src/deepctl_core/base_command.py +14 -2
- {deepctl_core-0.2.12 → deepctl_core-0.2.14}/src/deepctl_core/client.py +62 -3
- {deepctl_core-0.2.12 → deepctl_core-0.2.14}/src/deepctl_core/skill_generator.py +49 -20
- {deepctl_core-0.2.12 → deepctl_core-0.2.14}/src/deepctl_core.egg-info/PKG-INFO +2 -2
- {deepctl_core-0.2.12 → deepctl_core-0.2.14}/src/deepctl_core.egg-info/requires.txt +1 -1
- {deepctl_core-0.2.12 → deepctl_core-0.2.14}/README.md +0 -0
- {deepctl_core-0.2.12 → deepctl_core-0.2.14}/setup.cfg +0 -0
- {deepctl_core-0.2.12 → deepctl_core-0.2.14}/src/deepctl_core/__init__.py +0 -0
- {deepctl_core-0.2.12 → deepctl_core-0.2.14}/src/deepctl_core/base_group_command.py +0 -0
- {deepctl_core-0.2.12 → deepctl_core-0.2.14}/src/deepctl_core/config.py +0 -0
- {deepctl_core-0.2.12 → deepctl_core-0.2.14}/src/deepctl_core/models.py +0 -0
- {deepctl_core-0.2.12 → deepctl_core-0.2.14}/src/deepctl_core/output.py +0 -0
- {deepctl_core-0.2.12 → deepctl_core-0.2.14}/src/deepctl_core/plugin_env.py +0 -0
- {deepctl_core-0.2.12 → deepctl_core-0.2.14}/src/deepctl_core/plugin_manager.py +0 -0
- {deepctl_core-0.2.12 → deepctl_core-0.2.14}/src/deepctl_core/py.typed +0 -0
- {deepctl_core-0.2.12 → deepctl_core-0.2.14}/src/deepctl_core/timing.py +0 -0
- {deepctl_core-0.2.12 → deepctl_core-0.2.14}/src/deepctl_core.egg-info/SOURCES.txt +0 -0
- {deepctl_core-0.2.12 → deepctl_core-0.2.14}/src/deepctl_core.egg-info/dependency_links.txt +0 -0
- {deepctl_core-0.2.12 → deepctl_core-0.2.14}/src/deepctl_core.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: deepctl-core
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.14
|
|
4
4
|
Summary: Core components for deepctl
|
|
5
5
|
Author-email: Deepgram <devrel@deepgram.com>
|
|
6
6
|
Maintainer-email: Deepgram <devrel@deepgram.com>
|
|
@@ -16,7 +16,7 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
|
16
16
|
Requires-Python: >=3.10
|
|
17
17
|
Description-Content-Type: text/markdown
|
|
18
18
|
Requires-Dist: click>=8.0.0
|
|
19
|
-
Requires-Dist: deepgram-sdk>=
|
|
19
|
+
Requires-Dist: deepgram-sdk>=7.5.0
|
|
20
20
|
Requires-Dist: pydantic>=2.0.0
|
|
21
21
|
Requires-Dist: rich>=13.0.0
|
|
22
22
|
Requires-Dist: httpx>=0.24.0
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "deepctl-core"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.14" # x-release-please-version
|
|
8
8
|
description = "Core components for deepctl"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -23,7 +23,7 @@ keywords = ["deepgram", "core", "auth", "config", "client"]
|
|
|
23
23
|
requires-python = ">=3.10"
|
|
24
24
|
dependencies = [
|
|
25
25
|
"click>=8.0.0",
|
|
26
|
-
"deepgram-sdk>=
|
|
26
|
+
"deepgram-sdk>=7.5.0",
|
|
27
27
|
"pydantic>=2.0.0",
|
|
28
28
|
"rich>=13.0.0",
|
|
29
29
|
"httpx>=0.24.0",
|
|
@@ -12,6 +12,7 @@ from pydantic import BaseModel
|
|
|
12
12
|
from rich.console import Console
|
|
13
13
|
from rich.progress import Progress, SpinnerColumn, TextColumn
|
|
14
14
|
|
|
15
|
+
from .client import _split_base_url
|
|
15
16
|
from .config import Config
|
|
16
17
|
from .models import ProfileInfo, ProfilesResult
|
|
17
18
|
|
|
@@ -273,8 +274,14 @@ class AuthManager:
|
|
|
273
274
|
"Content-Type": "application/json",
|
|
274
275
|
}
|
|
275
276
|
|
|
277
|
+
# Verify against the configured base URL (not hardcoded prod), so a
|
|
278
|
+
# custom base (e.g. staging) validates its own keys correctly.
|
|
279
|
+
# Preserve the input scheme so a plaintext endpoint isn't forced
|
|
280
|
+
# onto TLS.
|
|
281
|
+
base = self.config.get_profile().base_url or "https://api.deepgram.com"
|
|
282
|
+
rest_scheme, _, host = _split_base_url(base)
|
|
276
283
|
response = self.client.get(
|
|
277
|
-
"
|
|
284
|
+
f"{rest_scheme}://{host}/v1/projects",
|
|
278
285
|
headers=headers,
|
|
279
286
|
)
|
|
280
287
|
|
|
@@ -56,7 +56,10 @@ class BaseCommand(ABC):
|
|
|
56
56
|
config = Config()
|
|
57
57
|
|
|
58
58
|
# Extract explicit credentials from kwargs if provided
|
|
59
|
-
|
|
59
|
+
# (or from the global --api-key option stored on ctx.obj)
|
|
60
|
+
explicit_api_key = kwargs.get("api_key") or (
|
|
61
|
+
ctx.obj.get("api_key") if ctx.obj else None
|
|
62
|
+
)
|
|
60
63
|
explicit_project_id = kwargs.get("project_id")
|
|
61
64
|
|
|
62
65
|
# Create auth manager with explicit credentials
|
|
@@ -121,7 +124,16 @@ class BaseCommand(ABC):
|
|
|
121
124
|
# Handle command result
|
|
122
125
|
if result is not None:
|
|
123
126
|
with TimingContext("output_processing"):
|
|
124
|
-
|
|
127
|
+
try:
|
|
128
|
+
self.output_result(result, config)
|
|
129
|
+
except (BrokenPipeError, OSError):
|
|
130
|
+
# Downstream stream closed (e.g. an MCP host disconnected
|
|
131
|
+
# stdio after `dg mcp` finished). Nothing useful to log
|
|
132
|
+
# here because the logger writes to the same closed stream.
|
|
133
|
+
pass
|
|
134
|
+
except ValueError as exc:
|
|
135
|
+
if "closed file" not in str(exc):
|
|
136
|
+
raise
|
|
125
137
|
|
|
126
138
|
except KeyboardInterrupt:
|
|
127
139
|
self._tag_telemetry_status("cancelled")
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import re
|
|
5
6
|
from pathlib import Path
|
|
6
7
|
from typing import TYPE_CHECKING, Any, cast
|
|
7
8
|
|
|
@@ -20,6 +21,22 @@ if TYPE_CHECKING:
|
|
|
20
21
|
console = Console()
|
|
21
22
|
|
|
22
23
|
|
|
24
|
+
def _split_base_url(base_url: str) -> tuple[str, str, str]:
|
|
25
|
+
"""Split a base URL into (rest_scheme, ws_scheme, host).
|
|
26
|
+
|
|
27
|
+
Preserves the transport implied by the input scheme so a plaintext
|
|
28
|
+
endpoint is not forced onto TLS: an ``http://`` base maps to ``http`` for
|
|
29
|
+
REST and ``ws`` for WebSocket; anything else (including a bare host) maps
|
|
30
|
+
to ``https``/``wss``. The host is returned without scheme or trailing slash.
|
|
31
|
+
"""
|
|
32
|
+
match = re.match(r"^([a-z]+)://", base_url)
|
|
33
|
+
scheme = match.group(1) if match else "https"
|
|
34
|
+
host = re.sub(r"^[a-z]+://", "", base_url).rstrip("/")
|
|
35
|
+
if scheme in ("http", "ws"):
|
|
36
|
+
return "http", "ws", host
|
|
37
|
+
return "https", "wss", host
|
|
38
|
+
|
|
39
|
+
|
|
23
40
|
class DeepgramClient:
|
|
24
41
|
"""Wrapper around Deepgram SDK with authentication integration."""
|
|
25
42
|
|
|
@@ -64,10 +81,18 @@ class DeepgramClient:
|
|
|
64
81
|
current_profile.base_url
|
|
65
82
|
and current_profile.base_url != "https://api.deepgram.com"
|
|
66
83
|
):
|
|
84
|
+
# Derive both the REST and WebSocket endpoints from the
|
|
85
|
+
# configured base URL, so a custom base works for batch REST
|
|
86
|
+
# *and* streaming. Preserve the transport implied by the input
|
|
87
|
+
# scheme (http→ws, https→wss) so a plaintext local/staging
|
|
88
|
+
# endpoint isn't forced onto TLS. agent_rest is required by the
|
|
89
|
+
# SDK environment.
|
|
90
|
+
rest_scheme, ws_scheme, host = _split_base_url(current_profile.base_url)
|
|
67
91
|
kwargs["environment"] = DeepgramClientEnvironment(
|
|
68
|
-
base=
|
|
69
|
-
production=
|
|
70
|
-
agent=
|
|
92
|
+
base=f"{rest_scheme}://{host}",
|
|
93
|
+
production=f"{ws_scheme}://{host}",
|
|
94
|
+
agent=f"{ws_scheme}://{host}",
|
|
95
|
+
agent_rest=f"{rest_scheme}://{host}",
|
|
71
96
|
)
|
|
72
97
|
|
|
73
98
|
client = DGClient(**kwargs)
|
|
@@ -158,6 +183,40 @@ class DeepgramClient:
|
|
|
158
183
|
except Exception as e:
|
|
159
184
|
raise ApiError(body=f"Text-to-speech failed: {e}")
|
|
160
185
|
|
|
186
|
+
def speak_text_stream(
|
|
187
|
+
self,
|
|
188
|
+
text: str,
|
|
189
|
+
model: str,
|
|
190
|
+
encoding: str | None = None,
|
|
191
|
+
sample_rate: float | None = None,
|
|
192
|
+
) -> Iterator[bytes]:
|
|
193
|
+
"""Stream TTS audio over the Flux v2 WebSocket (speak.v2.connect).
|
|
194
|
+
|
|
195
|
+
Yields raw audio chunks as they arrive. The streaming transport emits
|
|
196
|
+
raw (non-containerized) audio, so only linear16/mulaw/alaw encodings
|
|
197
|
+
apply and sample_rate is sent as the string the streaming API expects.
|
|
198
|
+
"""
|
|
199
|
+
from deepgram.speak.v2.types.speak_v2speak import SpeakV2Speak
|
|
200
|
+
|
|
201
|
+
connect_kwargs: dict[str, Any] = {"model": model}
|
|
202
|
+
if encoding:
|
|
203
|
+
connect_kwargs["encoding"] = encoding
|
|
204
|
+
if sample_rate:
|
|
205
|
+
connect_kwargs["sample_rate"] = str(int(sample_rate))
|
|
206
|
+
|
|
207
|
+
try:
|
|
208
|
+
with self.client.speak.v2.connect(**connect_kwargs) as conn:
|
|
209
|
+
conn.send_speak(SpeakV2Speak(type="Speak", text=text))
|
|
210
|
+
conn.send_flush()
|
|
211
|
+
conn.send_close()
|
|
212
|
+
for message in conn:
|
|
213
|
+
# Audio arrives as raw bytes; control messages are parsed
|
|
214
|
+
# objects, which we skip here.
|
|
215
|
+
if isinstance(message, bytes):
|
|
216
|
+
yield message
|
|
217
|
+
except Exception as e:
|
|
218
|
+
raise ApiError(body=f"Text-to-speech streaming failed: {e}")
|
|
219
|
+
|
|
161
220
|
# ── Text Intelligence (Read) ───────────────────────────────────
|
|
162
221
|
|
|
163
222
|
def analyze_text(
|
|
@@ -426,47 +426,76 @@ def render_developer_guide(
|
|
|
426
426
|
"- **Aura-2** — Latest generation. High quality, low latency, many voices."
|
|
427
427
|
)
|
|
428
428
|
lines.append("- **Aura** — Previous generation. Solid quality and performance.")
|
|
429
|
+
lines.append(
|
|
430
|
+
"- **Flux** — Conversational TTS on the Speak v2 WebSocket API "
|
|
431
|
+
"(streaming, turn-based). Voices like `flux-alexis-en`."
|
|
432
|
+
)
|
|
429
433
|
lines.append("")
|
|
430
434
|
lines.append("### Popular Voices")
|
|
431
435
|
lines.append("")
|
|
432
|
-
lines.append("
|
|
436
|
+
lines.append("Aura voices follow `aura-2-{name}-en`. Examples:")
|
|
433
437
|
lines.append("- `aura-2-andromeda-en`, `aura-2-arcas-en`, `aura-2-atlas-en`")
|
|
434
438
|
lines.append("- `aura-2-luna-en`, `aura-2-stella-en`, `aura-2-helios-en`")
|
|
435
439
|
lines.append("")
|
|
440
|
+
lines.append(
|
|
441
|
+
"Flux (Speak v2) voices follow `flux-{name}-en` (English at launch), "
|
|
442
|
+
"e.g. `flux-alexis-en`."
|
|
443
|
+
)
|
|
444
|
+
lines.append("")
|
|
436
445
|
lines.append("Full voice list: <https://developers.deepgram.com/docs/tts-models>")
|
|
437
446
|
lines.append("")
|
|
438
|
-
lines.append("### TTS
|
|
447
|
+
lines.append("### Aura TTS — Speak v1, batch REST (Python)")
|
|
439
448
|
lines.append("")
|
|
440
449
|
lines.append("```python")
|
|
441
|
-
lines.append("from deepgram import DeepgramClient
|
|
450
|
+
lines.append("from deepgram import DeepgramClient")
|
|
442
451
|
lines.append("")
|
|
443
|
-
lines.append('
|
|
452
|
+
lines.append('client = DeepgramClient(api_key="DEEPGRAM_API_KEY")')
|
|
444
453
|
lines.append("")
|
|
445
|
-
lines.append("
|
|
446
|
-
lines.append('
|
|
454
|
+
lines.append("audio = client.speak.v1.audio.generate(")
|
|
455
|
+
lines.append(' text="Hello from Deepgram!",')
|
|
456
|
+
lines.append(' model="aura-2-andromeda-en",')
|
|
457
|
+
lines.append(' encoding="mp3",')
|
|
447
458
|
lines.append(")")
|
|
459
|
+
lines.append('with open("output.mp3", "wb") as f:')
|
|
460
|
+
lines.append(" for chunk in audio:")
|
|
461
|
+
lines.append(" f.write(chunk)")
|
|
462
|
+
lines.append("```")
|
|
448
463
|
lines.append("")
|
|
449
|
-
lines.append(
|
|
450
|
-
lines.append(
|
|
451
|
-
lines.append(
|
|
452
|
-
lines.append("
|
|
453
|
-
lines.append("
|
|
464
|
+
lines.append("### Flux TTS — Speak v2, WebSocket streaming (Python)")
|
|
465
|
+
lines.append("")
|
|
466
|
+
lines.append("```python")
|
|
467
|
+
lines.append("from deepgram import DeepgramClient")
|
|
468
|
+
lines.append("from deepgram.speak.v2.types.speak_v2speak import SpeakV2Speak")
|
|
469
|
+
lines.append("")
|
|
470
|
+
lines.append('client = DeepgramClient(api_key="DEEPGRAM_API_KEY")')
|
|
471
|
+
lines.append("")
|
|
472
|
+
lines.append("with client.speak.v2.connect(")
|
|
473
|
+
lines.append(' model="flux-alexis-en", encoding="linear16", sample_rate="24000"')
|
|
474
|
+
lines.append(") as conn:")
|
|
475
|
+
lines.append(
|
|
476
|
+
' conn.send_speak(SpeakV2Speak(type="Speak", text="Hello from Flux!"))'
|
|
477
|
+
)
|
|
478
|
+
lines.append(" conn.send_flush()")
|
|
479
|
+
lines.append(" conn.send_close()")
|
|
480
|
+
lines.append(' with open("output.raw", "wb") as f:')
|
|
481
|
+
lines.append(" for message in conn:")
|
|
482
|
+
lines.append(" if isinstance(message, bytes):")
|
|
483
|
+
lines.append(" f.write(message) # raw linear16 PCM, 24kHz mono")
|
|
454
484
|
lines.append("```")
|
|
455
485
|
lines.append("")
|
|
456
|
-
lines.append("### TTS
|
|
486
|
+
lines.append("### Aura TTS — Speak v1 (JavaScript)")
|
|
457
487
|
lines.append("")
|
|
458
488
|
lines.append("```javascript")
|
|
459
489
|
lines.append('import { createClient } from "@deepgram/sdk";')
|
|
460
490
|
lines.append("")
|
|
461
|
-
lines.append('const
|
|
462
|
-
lines.append("")
|
|
463
|
-
lines.append("const response = await dg.speak.request(")
|
|
464
|
-
lines.append(' { text: "Hello from Deepgram!" },')
|
|
465
|
-
lines.append(' { model: "aura-2-andromeda-en" }')
|
|
466
|
-
lines.append(");")
|
|
491
|
+
lines.append('const client = createClient("DEEPGRAM_API_KEY");')
|
|
467
492
|
lines.append("")
|
|
468
|
-
lines.append("const
|
|
469
|
-
lines.append("
|
|
493
|
+
lines.append("const response = await client.speak.v1.audio.generate({")
|
|
494
|
+
lines.append(' text: "Hello from Deepgram!",')
|
|
495
|
+
lines.append(' model: "aura-2-andromeda-en",')
|
|
496
|
+
lines.append("});")
|
|
497
|
+
lines.append("const buffer = await response.arrayBuffer();")
|
|
498
|
+
lines.append("// Write buffer to a file or audio output")
|
|
470
499
|
lines.append("```")
|
|
471
500
|
lines.append("")
|
|
472
501
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: deepctl-core
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.14
|
|
4
4
|
Summary: Core components for deepctl
|
|
5
5
|
Author-email: Deepgram <devrel@deepgram.com>
|
|
6
6
|
Maintainer-email: Deepgram <devrel@deepgram.com>
|
|
@@ -16,7 +16,7 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
|
16
16
|
Requires-Python: >=3.10
|
|
17
17
|
Description-Content-Type: text/markdown
|
|
18
18
|
Requires-Dist: click>=8.0.0
|
|
19
|
-
Requires-Dist: deepgram-sdk>=
|
|
19
|
+
Requires-Dist: deepgram-sdk>=7.5.0
|
|
20
20
|
Requires-Dist: pydantic>=2.0.0
|
|
21
21
|
Requires-Dist: rich>=13.0.0
|
|
22
22
|
Requires-Dist: httpx>=0.24.0
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|