Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -24,7 +24,7 @@ pip install calle-ai
Pin the current stable release when your deployment process requires exact package reproducibility:

```bash
pip install calle-ai==0.6.0
pip install calle-ai==0.7.0
```

Use a local checkout for development and package smoke tests:
Expand DownExpand Up@@ -113,6 +113,7 @@ run = client.goals.run_and_wait(
)

if run["result"] is not None:
print(run["call_id"])
print(run["result"])
else:
print(run["error"])
Expand DownExpand Up@@ -182,11 +183,11 @@ Manual stable PyPI publish:
```bash
python -m venv .venv
. .venv/bin/activate
pip install calle-ai==0.6.0
pip install calle-ai==0.7.0
python -c 'from calle import CalleClient; c = CalleClient(api_key="smoke"); assert callable(c.goals.run_and_wait); c.close()'
```

The current stable version is `0.6.0`. Do not reuse a previously published
The current stable version is `0.7.0`. Do not reuse a previously published
PyPI version.

## Project Documents
Expand Down
4 changes: 2 additions & 2 deletions RELEASE.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,7 +13,7 @@ calle-ai==0.1.0b1
The current production PyPI release version is:

```text
calle-ai==0.6.0
calle-ai==0.7.0
```

For this release, use token-based PyPI publishing with the GitHub Actions secret `PYPI_API_TOKEN`.
Expand DownExpand Up@@ -67,7 +67,7 @@ tmpdir="$(mktemp -d)"
python -m venv "$tmpdir/.venv"
. "$tmpdir/.venv/bin/activate"
python -m pip install --upgrade pip
python -m pip install calle-ai==0.6.0
python -m pip install calle-ai==0.7.0
python -c 'from calle import CalleClient; c = CalleClient(api_key="smoke"); assert callable(c.goals.run_and_wait); c.close()'
```

Expand Down
2 changes: 1 addition & 1 deletion openapi-python-client.yml
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
project_name_override: calle-generated
package_name_override: generated
package_version_override: 0.6.0
package_version_override: 0.7.0
literal_enums: true
generate_all_tags: true
use_path_prefixes_for_title_model_names: false
Expand Down
25 changes: 18 additions & 7 deletions openapi/calle.openapi.yaml
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
openapi: 3.1.0
info:
title: CALL-E Developer API
version: 0.6.0
version: 0.7.0
description: Developer API contract used by the CALL-E TypeScript and Python SDKs.
servers:
- url: https://api.heycall-e.com
Expand DownExpand Up@@ -32,12 +32,8 @@ paths:
recipients:
- phones:
- "<RECIPIENT_1_E164_PHONE>"
region: US
locale: en-US
- phones:
- "<RECIPIENT_2_E164_PHONE>"
region: US
locale: en-US
result_schema:
type: object
required:
Expand DownExpand Up@@ -328,6 +324,8 @@ paths:
`after` value; clients must not parse or construct cursor values. `title` and `description`
help operators recognize each published workflow, but integrations should still store the
intended `goal_id` at publish time and must not execute the first list item blindly.
Candidates whose published RunSpec is not execution-ready are omitted without preventing
later executable Goals from appearing in the page.
parameters:
- $ref: "#/components/parameters/GoalListLimit"
- $ref: "#/components/parameters/GoalListAfter"
Expand DownExpand Up@@ -399,8 +397,6 @@ paths:
$ref: "#/components/responses/ErrorResponse"
"403":
$ref: "#/components/responses/ErrorResponse"
"409":
$ref: "#/components/responses/ErrorResponse"
"429":
$ref: "#/components/responses/ErrorResponse"
"500":
Expand DownExpand Up@@ -520,6 +516,7 @@ paths:
id: rgrp_delivery_ord_8472
goal_id: goal_delivery_confirmation
run_id: run_delivery_ord_8472
call_id: null
status: queued
run_spec:
id: rspec_delivery_v4
Expand DownExpand Up@@ -597,6 +594,7 @@ paths:
id: rgrp_delivery_ord_8472
goal_id: goal_delivery_confirmation
run_id: run_delivery_ord_8472
call_id: calling_call_delivery_ord_8472
status: completed
run_spec:
id: rspec_delivery_v4
Expand All@@ -614,6 +612,7 @@ paths:
id: rgrp_delivery_ord_8472
goal_id: goal_delivery_confirmation
run_id: run_delivery_ord_8472
call_id: null
status: failed
run_spec:
id: rspec_delivery_v4
Expand DownExpand Up@@ -998,6 +997,7 @@ components:
- id
- goal_id
- run_id
- call_id
- run_spec
- status
- result
Expand All@@ -1021,6 +1021,17 @@ components:
type: string
description: Internal execution member exposed for correlation; do not use it in the Goal Run polling path.
minLength: 1
call_id:
type:
- string
- "null"
description: |-
Calling call identifier selected for this Goal Run when that trusted fact is available,
or `null` before a call identifier is persisted or when no identifier is available. This
is different from the Goal Run `id` and nested `run_id`; it does not expose other provider
diagnostics and must not be treated as an independent answered-call boolean.
minLength: 1
maxLength: 255
run_spec:
description: Read-only identity and version of the exact RunSpec pinned by this Run.
$ref: "#/components/schemas/GoalRunSpecSnapshot"
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
[project]
name = "calle-ai"
version = "0.6.0"
version = "0.7.0"
description = "Python server SDK for the CALL-E Developer API."
readme = "README.md"
requires-python = ">=3.11"
Expand Down
9 changes: 7 additions & 2 deletions scripts/verify_openapi_contract.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,7 +49,7 @@ def main() -> None:
"unexpected title",
)
assert_contract(
spec.get("info", {}).get("version") == "0.6.0",
spec.get("info", {}).get("version") == "0.7.0",
"unexpected API version",
)

Expand DownExpand Up@@ -82,7 +82,7 @@ def main() -> None:
"method": "get",
"operation_id": "listGoals",
"response_schema": "#/components/schemas/GoalList",
"error_statuses": ["400", "401", "403", "409", "429", "500"],
"error_statuses": ["400", "401", "403", "429", "500"],
},
{
"path": "/v1/goals/{goal_id}",
Expand DownExpand Up@@ -319,6 +319,7 @@ def main() -> None:
"id",
"goal_id",
"run_id",
"call_id",
"run_spec",
"status",
"result",
Expand All@@ -330,6 +331,10 @@ def main() -> None:
property_name in goal_run_properties,
f"GoalRun missing {property_name}",
)
assert_contract(
set(goal_run_properties["call_id"].get("type", [])) == {"string", "null"},
"GoalRun.call_id must be nullable string",
)

goal_run_parameter_refs = parameter_refs(
spec,
Expand Down
13 changes: 8 additions & 5 deletions src/calle/generated/api/goals/list_goals.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -58,11 +58,6 @@ def _parse_response(

return response_403

if response.status_code == 409:
response_409 = ErrorEnvelope.from_dict(response.json())

return response_409

if response.status_code == 429:
response_429 = ErrorEnvelope.from_dict(response.json())

Expand DownExpand Up@@ -106,6 +101,8 @@ def sync_detailed(
`after` value; clients must not parse or construct cursor values. `title` and `description`
help operators recognize each published workflow, but integrations should still store the
intended `goal_id` at publish time and must not execute the first list item blindly.
Candidates whose published RunSpec is not execution-ready are omitted without preventing
later executable Goals from appearing in the page.

Args:
limit (int | Unset): Default: 20.
Expand DownExpand Up@@ -147,6 +144,8 @@ def sync(
`after` value; clients must not parse or construct cursor values. `title` and `description`
help operators recognize each published workflow, but integrations should still store the
intended `goal_id` at publish time and must not execute the first list item blindly.
Candidates whose published RunSpec is not execution-ready are omitted without preventing
later executable Goals from appearing in the page.

Args:
limit (int | Unset): Default: 20.
Expand DownExpand Up@@ -183,6 +182,8 @@ async def asyncio_detailed(
`after` value; clients must not parse or construct cursor values. `title` and `description`
help operators recognize each published workflow, but integrations should still store the
intended `goal_id` at publish time and must not execute the first list item blindly.
Candidates whose published RunSpec is not execution-ready are omitted without preventing
later executable Goals from appearing in the page.

Args:
limit (int | Unset): Default: 20.
Expand DownExpand Up@@ -222,6 +223,8 @@ async def asyncio(
`after` value; clients must not parse or construct cursor values. `title` and `description`
help operators recognize each published workflow, but integrations should still store the
intended `goal_id` at publish time and must not execute the first list item blindly.
Candidates whose published RunSpec is not execution-ready are omitted without preventing
later executable Goals from appearing in the page.

Args:
limit (int | Unset): Default: 20.
Expand Down
17 changes: 17 additions & 0 deletions src/calle/generated/models/goal_run.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -33,6 +33,10 @@ class GoalRun:
id (str): Public Goal Run identity. Persist this value and use it as `goal_run_id` when polling.
goal_id (str): Goal identity supplied in the create path.
run_id (str): Internal execution member exposed for correlation; do not use it in the Goal Run polling path.
call_id (None | str): Calling call identifier selected for this Goal Run when that trusted fact is available,
or `null` before a call identifier is persisted or when no identifier is available. This
is different from the Goal Run `id` and nested `run_id`; it does not expose other provider
diagnostics and must not be treated as an independent answered-call boolean.
run_spec (GoalRunSpecSnapshot): Exact immutable RunSpec identity and version pinned by a Goal Run.
status (GoalRunStatus): Stable telephone execution state. `queued` and `in_progress` are non-terminal;
`completed`,
Expand All@@ -53,6 +57,7 @@ class GoalRun:
id: str
goal_id: str
run_id: str
call_id: None | str
run_spec: GoalRunSpecSnapshot
status: GoalRunStatus
result: GoalRunResultType0 | None
Expand All@@ -72,6 +77,9 @@ def to_dict(self) -> dict[str, Any]:

run_id = self.run_id

call_id: None | str
call_id = self.call_id

run_spec = self.run_spec.to_dict()

status: str = self.status
Expand DownExpand Up@@ -104,6 +112,7 @@ def to_dict(self) -> dict[str, Any]:
"id": id,
"goal_id": goal_id,
"run_id": run_id,
"call_id": call_id,
"run_spec": run_spec,
"status": status,
"result": result,
Expand All@@ -130,6 +139,13 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:

run_id = d.pop("run_id")

def _parse_call_id(data: object) -> None | str:
if data is None:
return data
return cast(None | str, data)

call_id = _parse_call_id(d.pop("call_id"))

run_spec = GoalRunSpecSnapshot.from_dict(d.pop("run_spec"))

status = check_goal_run_status(d.pop("status"))
Expand DownExpand Up@@ -186,6 +202,7 @@ def _parse_completed_at(data: object) -> datetime.datetime | None:
id=id,
goal_id=goal_id,
run_id=run_id,
call_id=call_id,
run_spec=run_spec,
status=status,
result=result,
Expand Down
4 changes: 4 additions & 0 deletions tests/test_goals.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -37,6 +37,7 @@
"id": "rgrp_delivery_8472",
"goal_id": "goal_delivery",
"run_id": "run_delivery_8472",
"call_id": None,
"run_spec": {"id": "rspec_delivery_v4", "version": 4},
"status": "queued",
"result": None,
Expand All@@ -53,6 +54,7 @@ def test_generated_goal_models_parse_public_api_examples() -> None:
assert goal.id == "goal_delivery"
assert goal.published_run_spec.version == 4
assert run.id == "rgrp_delivery_8472"
assert run.call_id is None
assert run.run_spec.id == "rspec_delivery_v4"


Expand DownExpand Up@@ -150,6 +152,7 @@ def test_wait_for_result_ignores_completed_status_until_result_exists() -> None:
}
succeeded = {
**materializing,
"call_id": "calling_call_delivery_8472",
"result": {"delivery_outcome": "confirmed"},
}
route = respx.get("https://api.heycall-e.com/v1/goals/goal_delivery/runs/rgrp_delivery_8472").mock(
Expand All@@ -168,6 +171,7 @@ def test_wait_for_result_ignores_completed_status_until_result_exists() -> None:
)

assert run["result"] == {"delivery_outcome": "confirmed"}
assert run["call_id"] == "calling_call_delivery_8472"
assert route.call_count == 2


Expand Down
2 changes: 1 addition & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.