Skip to content

Commit 5fa3e5e

Browse files
feat(5.2.0): agent versioning v2 helper + waves unified-tts + [8] init cleanup
On top of the Fern regen (dnc restored via docs #315): - Versioning helper (smallestai.atoms.helpers.Versioning): publish_and_wait / wait_for_commit (publish runs an async security scan -> list-then-poll), edit_and_publish (one-call config edit), update_draft with a typed 409 discriminator -> MigrationRequiredError / DraftConflictError (handles dev-rich data.conflict AND prod-lean errors[]) / BaseRevisionUnavailableError. - Wire Versioning into atoms.helpers; add unit test + live E2E + verbose API probe + cookbook example. - Waves: adopt the unified-tts client (regen-owned again; the old streaming/SampleRate override is obsolete since the unified tts client streams bytes natively). Drop removed lightning_v2tts/v31tts refs from the waves shim __init__. - [8]: exclude_types_from_init_exports=false -> remove the __init__ re-export stopgaps, use native exports. - Bump 5.1.1 -> 5.2.0 + changelog. Verified live on api.smallest.ai: versioning e2e 15/15, dnc.list, waves synth; mypy clean, verify passed, pytest 245 passed.
1 parent 0e21531 commit 5fa3e5e

15 files changed

Lines changed: 4484 additions & 5591 deletions

File tree

‎.fernignore‎

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,15 @@ src/smallestai/waves/stream_tts.py
1313
src/smallestai/waves/types/sample_rate.py
1414

1515
# Files with non-IR-expressible customizations
16-
# (shim block in waves/__init__.py; SampleRate type override in client.py/raw_client.py)
16+
# (shim block in waves/__init__.py lazy-loads the WavesStreamingTTS compat shim).
17+
# client.py/raw_client.py are now regen-owned: the unified `tts` client streams bytes
18+
# natively (verified 282KB audio on prod), so the old SampleRate/streaming override is
19+
# obsolete. Dropped from .fernignore in 5.2.0 so future regens keep them current.
1720
src/smallestai/waves/__init__.py
18-
src/smallestai/waves/client.py
19-
src/smallestai/waves/raw_client.py
20-
21-
# Sub-package __init__ type re-exports: generator 5.12.12 strips sub-package type
22-
# exports (exclude_types_from_init_exports), but generated wire tests import these
23-
# types from the package root. Preserve the re-exports. See SDK_ESCALATIONS.log [1].
24-
src/smallestai/atoms/webhooks/__init__.py
25-
src/smallestai/atoms/prompt_scoring/__init__.py
26-
src/smallestai/atoms/__init__.py
21+
22+
# (Sub-package __init__ stopgaps removed in 5.2.0: exclude_types_from_init_exports
23+
# was flipped to false, so the generator now exports these types natively and the
24+
# hand-written re-export stopgaps are obsolete. See SDK_ESCALATIONS.log [8].)
2725

2826
# Customer-owned CI / release automation
2927
.github/workflows/**

‎changelog.md‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,25 @@
1+
## 5.2.0 - 2026-07-24
2+
3+
Agent Versioning v2 (branch/revision API) + new namespaces, plus an ergonomic versioning helper.
4+
Additive; generator pinned to 5.12.12.
5+
6+
* **Agent Versioning v2** — new `agent_versioning_branches` (`create_branch`, `list`, `get`,
7+
`rename`, `archive`, `make_live`, `get_draft`, `update_draft`, `discard_draft`, `publish_draft`,
8+
`cancel_publish`, `test_call`) and `agent_versioning_revisions` (`list`, `get`, `get_history`,
9+
`restore`, `diff`) clients. `update_draft` takes typed config kwargs (`global_prompt`,
10+
`first_message`, `synthesizer`, …). The v1 `agent_versioning_drafts`/`agent_versioning_versions`
11+
write endpoints are deprecated under the branch model (they return `409
12+
versioning_v2_migration_required`).
13+
* **Versioning helper** (`from smallestai.atoms.helpers import Versioning`): `publish_and_wait` /
14+
`wait_for_commit` (publishing runs a security scan asynchronously — this polls until the revision
15+
is `published`), `edit_and_publish` (one-call config edit), and typed conflict handling
16+
(`DraftConflictError`, `MigrationRequiredError`, `BaseRevisionUnavailableError`).
17+
* **New namespaces**: `call_actions`, `integrations`, `concurrency`, `disposition_metric_templates`,
18+
`realtime`.
19+
* Generator: `exclude_types_from_init_exports` flipped to `false`.
20+
* Verified live end-to-end against `api.smallest.ai` (fork → edit → publish → scan → test-call →
21+
make-live → restore), via both raw endpoints and the generated SDK.
22+
123
## 5.1.1 - 2026-07-15
224

325
Patch — fix the streaming-TTS compat shim (`WavesStreamingTTS`/`TTSConfig`).
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
"""Build a voice agent and iterate on it safely, with the smallestai SDK.
2+
3+
The 90% path is one call: create_agent(...) stands up a fully-configured, LIVE
4+
agent. You do NOT need the versioning API to create or run an agent.
5+
6+
Versioning (branches / drafts / revisions) is the layer for CHANGING an agent's
7+
config safely after it exists: edits are staged as a draft, published as an
8+
immutable revision (with a security scan), and can be staged on a branch or
9+
rolled back. Under the branch model, runtime-config edits go through this flow;
10+
`update_agent` only changes metadata (name, description, inbound toggle, ...).
11+
12+
pip install smallestai
13+
export SMALLEST_API_KEY=sk_...
14+
python examples/agent_versioning_lifecycle.py
15+
"""
16+
import os
17+
18+
from smallestai import SmallestAI
19+
from smallestai.atoms.helpers.versioning import Versioning, DraftConflictError
20+
21+
client = SmallestAI(api_key=os.environ["SMALLEST_API_KEY"])
22+
23+
# 1. CREATE — one call, fully configured, live immediately. No versioning needed.
24+
agent_id = client.atoms.agents.create_agent(
25+
name="receptionist",
26+
global_prompt="You are a friendly receptionist. Book appointments and answer questions.",
27+
first_message="Hi, how can I help?",
28+
language={"switching": {"isEnabled": False}},
29+
).data
30+
print("agent (live):", agent_id)
31+
32+
# You can place calls right now — the agent is serving the config above.
33+
# client.atoms.calls.start_an_outbound_call(agent_id=agent_id, to_phone="+15551234567")
34+
35+
# ------------------------------------------------------------------------------
36+
# 2. EDIT the config later — this is where versioning comes in.
37+
# ------------------------------------------------------------------------------
38+
v = Versioning(client)
39+
40+
# Find the live Main branch (id + isDefault live on the inner .branch).
41+
branches = v.branches.list(id=agent_id).data.branches
42+
main_id = next(b for b in branches if b.branch.is_default).branch.id
43+
44+
# Quick edit: change the config and publish in one call (draft -> publish -> live,
45+
# security scan handled). This is the simple "update my agent" path.
46+
revision = v.edit_and_publish(
47+
agent_id, main_id,
48+
global_prompt="You are a warm, concise receptionist. Confirm details before booking.",
49+
label="tone tweak",
50+
)
51+
print("published revision:", revision.id, "status:", revision.status)
52+
53+
# Test the branch before/after going live.
54+
test = v.branches.test_call(id=agent_id, branch_id=main_id, mode="webcall")
55+
print("test call id:", test.data.call_id)
56+
57+
# ------------------------------------------------------------------------------
58+
# 3. ADVANCED — stage a big change on a fork, promote it, roll back.
59+
# ------------------------------------------------------------------------------
60+
# Fork Main so real traffic keeps hitting the live config while you experiment.
61+
staging_id = v.branches.create_branch(id=agent_id, source_branch_id=main_id, name="staging").data.id
62+
v.edit_and_publish(agent_id, staging_id, global_prompt="New experimental prompt.", label="experiment")
63+
v.branches.make_live(id=agent_id, branch_id=staging_id) # staging live, Main flips to not-live
64+
65+
# Roll back: restore an older revision as a new head revision on the live branch.
66+
older = v.revisions.list(id=agent_id, branch_id=staging_id).data.revisions[-1].id
67+
v.revisions.restore(id=agent_id, branch_id=staging_id, revision_id=older)
68+
69+
# ------------------------------------------------------------------------------
70+
# 4. Safe concurrent edits — optimistic concurrency.
71+
# ------------------------------------------------------------------------------
72+
draft = v.branches.get_draft(id=agent_id, branch_id=main_id).data
73+
try:
74+
v.edit_and_publish(
75+
agent_id, main_id,
76+
expected_revision=draft.latest.draft_revision, # only apply if nobody edited since
77+
global_prompt="Applied only if no conflicting edit landed first.",
78+
)
79+
except DraftConflictError as e:
80+
print(f"conflict: based on r{e.expected_revision}, latest is r{e.latest_revision}")
81+
for d in e.diffs:
82+
print(" changed section:", d["section"])
83+
# Rebase on e.latest_revision, or retry without expected_revision to force-overwrite.

‎pyproject.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ dynamic = ["version"]
55

66
[tool.poetry]
77
name = "smallestai"
8-
version = "5.1.1"
8+
version = "5.2.0"
99
description = ""
1010
readme = "README.md"
1111
authors = []

0 commit comments

Comments
 (0)