fora-sdk 0.1.7__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.
- fora_sdk-0.1.7/.gitignore +3 -0
- fora_sdk-0.1.7/LICENSE +24 -0
- fora_sdk-0.1.7/PKG-INFO +103 -0
- fora_sdk-0.1.7/README.md +77 -0
- fora_sdk-0.1.7/pyproject.toml +48 -0
- fora_sdk-0.1.7/src/fora_sdk/__init__.py +35 -0
- fora_sdk-0.1.7/src/fora_sdk/_generated_client.py +114 -0
- fora_sdk-0.1.7/src/fora_sdk/_generated_models.py +801 -0
- fora_sdk-0.1.7/src/fora_sdk/_validation.py +166 -0
- fora_sdk-0.1.7/src/fora_sdk/client.py +177 -0
- fora_sdk-0.1.7/src/fora_sdk/codegen.py +332 -0
- fora_sdk-0.1.7/src/fora_sdk/errors.py +32 -0
- fora_sdk-0.1.7/src/fora_sdk/typed/__init__.py +27 -0
- fora_sdk-0.1.7/src/fora_sdk/typed/_base.py +49 -0
- fora_sdk-0.1.7/src/fora_sdk/typed/agent_node.py +96 -0
- fora_sdk-0.1.7/src/fora_sdk/typed/end_call.py +82 -0
- fora_sdk-0.1.7/src/fora_sdk/typed/global_node.py +38 -0
- fora_sdk-0.1.7/src/fora_sdk/typed/qa.py +87 -0
- fora_sdk-0.1.7/src/fora_sdk/typed/start_call.py +142 -0
- fora_sdk-0.1.7/src/fora_sdk/typed/trigger.py +55 -0
- fora_sdk-0.1.7/src/fora_sdk/typed/tuner.py +50 -0
- fora_sdk-0.1.7/src/fora_sdk/typed/webhook.py +78 -0
- fora_sdk-0.1.7/src/fora_sdk/workflow.py +247 -0
fora_sdk-0.1.7/LICENSE
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
BSD 2-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025, Zansat Technologies Private Limited
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
16
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
17
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
18
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
19
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
20
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
21
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
22
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
23
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
24
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
fora_sdk-0.1.7/PKG-INFO
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fora-sdk
|
|
3
|
+
Version: 0.1.7
|
|
4
|
+
Summary: Typed builder for Fora voice-AI workflows
|
|
5
|
+
Project-URL: Homepage, https://github.com/Phoraapp/fora
|
|
6
|
+
Project-URL: Repository, https://github.com/Phoraapp/fora
|
|
7
|
+
Author-email: Phoraapp <phoraapp@gmail.com>
|
|
8
|
+
License: BSD-2-Clause
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: agent,fora,llm,sdk,voice-ai,workflow
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Requires-Dist: httpx>=0.27
|
|
22
|
+
Requires-Dist: pydantic>=2.0
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
# fora-sdk
|
|
28
|
+
|
|
29
|
+
Typed builder for Fora voice-AI workflows. Fetches the node-spec catalog from
|
|
30
|
+
the Fora backend at session start, validates every call against it at the
|
|
31
|
+
call site, and produces `ReactFlowDTO`-compatible JSON.
|
|
32
|
+
|
|
33
|
+
## Install
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install fora-sdk
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
For local development against a checked-out monorepo:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pip install -e sdk/python/
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Usage
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
from fora_sdk import ForaClient, Workflow
|
|
49
|
+
|
|
50
|
+
with ForaClient(base_url="http://localhost:8000", api_key="...") as client:
|
|
51
|
+
wf = Workflow(client=client, name="loan_qualification")
|
|
52
|
+
|
|
53
|
+
start = wf.add(
|
|
54
|
+
type="startCall",
|
|
55
|
+
name="greeting",
|
|
56
|
+
prompt="You are Sarah from Acme Loans. Greet the caller warmly.",
|
|
57
|
+
greeting_type="text",
|
|
58
|
+
greeting="Hi {{first_name}}, this is Sarah.",
|
|
59
|
+
)
|
|
60
|
+
qualify = wf.add(
|
|
61
|
+
type="agentNode",
|
|
62
|
+
name="qualify",
|
|
63
|
+
prompt="Ask about loan amount and timeline.",
|
|
64
|
+
)
|
|
65
|
+
done = wf.add(type="endCall", name="done", prompt="Thank the caller.")
|
|
66
|
+
|
|
67
|
+
wf.edge(start, qualify, label="interested", condition="Caller expressed interest.")
|
|
68
|
+
wf.edge(qualify, done, label="done", condition="Qualification complete.")
|
|
69
|
+
|
|
70
|
+
client.save_workflow(workflow_id=123, workflow=wf)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## What gets validated at the call site
|
|
74
|
+
|
|
75
|
+
The SDK fetches the spec for each node type via `get_node_type` and raises
|
|
76
|
+
`ValidationError` immediately when:
|
|
77
|
+
|
|
78
|
+
- an unknown field is passed (catches typos)
|
|
79
|
+
- a required field is missing or empty
|
|
80
|
+
- a scalar type is wrong (e.g., string for a boolean)
|
|
81
|
+
- an `options` value isn't in the allowed list
|
|
82
|
+
|
|
83
|
+
When a spec carries an `llm_hint`, the hint is appended to the error message so
|
|
84
|
+
an LLM agent can self-correct on retry:
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
tool_uuids: expected tool_refs, got str
|
|
88
|
+
Hint: List of tool UUIDs from `list_tools`.
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Server-side Pydantic validators run on save and surface anything the SDK lets
|
|
92
|
+
through (compound invariants, cross-field rules).
|
|
93
|
+
|
|
94
|
+
## Environment
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
FORA_API_URL=http://localhost:8000 # default (legacy DOGRAH_API_URL also accepted)
|
|
98
|
+
FORA_API_KEY=sk-... # sent as X-API-Key (legacy DOGRAH_API_KEY also accepted)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## License
|
|
102
|
+
|
|
103
|
+
BSD 2-Clause — see `LICENSE`.
|
fora_sdk-0.1.7/README.md
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# fora-sdk
|
|
2
|
+
|
|
3
|
+
Typed builder for Fora voice-AI workflows. Fetches the node-spec catalog from
|
|
4
|
+
the Fora backend at session start, validates every call against it at the
|
|
5
|
+
call site, and produces `ReactFlowDTO`-compatible JSON.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install fora-sdk
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
For local development against a checked-out monorepo:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pip install -e sdk/python/
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
```python
|
|
22
|
+
from fora_sdk import ForaClient, Workflow
|
|
23
|
+
|
|
24
|
+
with ForaClient(base_url="http://localhost:8000", api_key="...") as client:
|
|
25
|
+
wf = Workflow(client=client, name="loan_qualification")
|
|
26
|
+
|
|
27
|
+
start = wf.add(
|
|
28
|
+
type="startCall",
|
|
29
|
+
name="greeting",
|
|
30
|
+
prompt="You are Sarah from Acme Loans. Greet the caller warmly.",
|
|
31
|
+
greeting_type="text",
|
|
32
|
+
greeting="Hi {{first_name}}, this is Sarah.",
|
|
33
|
+
)
|
|
34
|
+
qualify = wf.add(
|
|
35
|
+
type="agentNode",
|
|
36
|
+
name="qualify",
|
|
37
|
+
prompt="Ask about loan amount and timeline.",
|
|
38
|
+
)
|
|
39
|
+
done = wf.add(type="endCall", name="done", prompt="Thank the caller.")
|
|
40
|
+
|
|
41
|
+
wf.edge(start, qualify, label="interested", condition="Caller expressed interest.")
|
|
42
|
+
wf.edge(qualify, done, label="done", condition="Qualification complete.")
|
|
43
|
+
|
|
44
|
+
client.save_workflow(workflow_id=123, workflow=wf)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## What gets validated at the call site
|
|
48
|
+
|
|
49
|
+
The SDK fetches the spec for each node type via `get_node_type` and raises
|
|
50
|
+
`ValidationError` immediately when:
|
|
51
|
+
|
|
52
|
+
- an unknown field is passed (catches typos)
|
|
53
|
+
- a required field is missing or empty
|
|
54
|
+
- a scalar type is wrong (e.g., string for a boolean)
|
|
55
|
+
- an `options` value isn't in the allowed list
|
|
56
|
+
|
|
57
|
+
When a spec carries an `llm_hint`, the hint is appended to the error message so
|
|
58
|
+
an LLM agent can self-correct on retry:
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
tool_uuids: expected tool_refs, got str
|
|
62
|
+
Hint: List of tool UUIDs from `list_tools`.
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Server-side Pydantic validators run on save and surface anything the SDK lets
|
|
66
|
+
through (compound invariants, cross-field rules).
|
|
67
|
+
|
|
68
|
+
## Environment
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
FORA_API_URL=http://localhost:8000 # default (legacy DOGRAH_API_URL also accepted)
|
|
72
|
+
FORA_API_KEY=sk-... # sent as X-API-Key (legacy DOGRAH_API_KEY also accepted)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## License
|
|
76
|
+
|
|
77
|
+
BSD 2-Clause — see `LICENSE`.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "fora-sdk"
|
|
3
|
+
version = "0.1.7"
|
|
4
|
+
description = "Typed builder for Fora voice-AI workflows"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.10"
|
|
7
|
+
license = { text = "BSD-2-Clause" }
|
|
8
|
+
authors = [
|
|
9
|
+
{ name = "Phoraapp", email = "phoraapp@gmail.com" },
|
|
10
|
+
]
|
|
11
|
+
keywords = ["fora", "voice-ai", "workflow", "sdk", "llm", "agent"]
|
|
12
|
+
classifiers = [
|
|
13
|
+
"Development Status :: 3 - Alpha",
|
|
14
|
+
"Intended Audience :: Developers",
|
|
15
|
+
"License :: OSI Approved :: BSD License",
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
"Programming Language :: Python :: 3.10",
|
|
18
|
+
"Programming Language :: Python :: 3.11",
|
|
19
|
+
"Programming Language :: Python :: 3.12",
|
|
20
|
+
"Programming Language :: Python :: 3.13",
|
|
21
|
+
"Topic :: Software Development :: Libraries",
|
|
22
|
+
]
|
|
23
|
+
dependencies = [
|
|
24
|
+
"httpx>=0.27",
|
|
25
|
+
"pydantic>=2.0",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
[project.optional-dependencies]
|
|
29
|
+
dev = [
|
|
30
|
+
"pytest>=7.0",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[project.urls]
|
|
34
|
+
Homepage = "https://github.com/Phoraapp/fora"
|
|
35
|
+
Repository = "https://github.com/Phoraapp/fora"
|
|
36
|
+
|
|
37
|
+
[project.scripts]
|
|
38
|
+
fora-sdk-codegen = "fora_sdk.codegen:main"
|
|
39
|
+
|
|
40
|
+
[build-system]
|
|
41
|
+
requires = ["hatchling"]
|
|
42
|
+
build-backend = "hatchling.build"
|
|
43
|
+
|
|
44
|
+
[tool.hatch.build.targets.wheel]
|
|
45
|
+
packages = ["src/fora_sdk"]
|
|
46
|
+
|
|
47
|
+
[tool.pytest.ini_options]
|
|
48
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""Fora SDK — typed builder for voice-AI workflows.
|
|
2
|
+
|
|
3
|
+
Runtime SDK: fetches the spec catalog from the Fora backend at session
|
|
4
|
+
start and validates every `Workflow.add()` call against it. LLMs don't
|
|
5
|
+
need to import per-node-type classes — the `type` argument is a string
|
|
6
|
+
keyed against the fetched spec catalog.
|
|
7
|
+
|
|
8
|
+
from fora_sdk import ForaClient, Workflow
|
|
9
|
+
|
|
10
|
+
with ForaClient(base_url="http://localhost:8000", api_key=...) as client:
|
|
11
|
+
wf = Workflow(client=client, name="loan_qualification")
|
|
12
|
+
start = wf.add(type="startCall", name="greeting", prompt="...")
|
|
13
|
+
qualify = wf.add(type="agentNode", name="qualify", prompt="...")
|
|
14
|
+
wf.edge(start, qualify, label="interested", condition="...")
|
|
15
|
+
client.save_workflow(workflow_id=123, workflow=wf)
|
|
16
|
+
|
|
17
|
+
For typed IDE autocomplete, generate per-node dataclasses via the SDK
|
|
18
|
+
codegen (Phase 6) — the runtime and typed SDKs share this same core.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from .client import ForaClient
|
|
22
|
+
from .errors import ApiError, ForaSdkError, SpecMismatchError, ValidationError
|
|
23
|
+
from .typed._base import TypedNode
|
|
24
|
+
from .workflow import NodeRef, Workflow
|
|
25
|
+
|
|
26
|
+
__all__ = [
|
|
27
|
+
"ApiError",
|
|
28
|
+
"ForaClient",
|
|
29
|
+
"ForaSdkError",
|
|
30
|
+
"NodeRef",
|
|
31
|
+
"SpecMismatchError",
|
|
32
|
+
"TypedNode",
|
|
33
|
+
"ValidationError",
|
|
34
|
+
"Workflow",
|
|
35
|
+
]
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"""GENERATED — do not edit. Source: filtered OpenAPI from `api.app`.
|
|
2
|
+
|
|
3
|
+
Regenerate with `./scripts/generate_sdk.sh`.
|
|
4
|
+
|
|
5
|
+
`ForaClient` mixes in this class to get HTTP methods for every route
|
|
6
|
+
decorated with `sdk_expose(...)` on the backend. Request/response types
|
|
7
|
+
come from `_generated_models` (datamodel-codegen output).
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
from fora_sdk._generated_models import (
|
|
15
|
+
CreateToolRequest,
|
|
16
|
+
CreateWorkflowRequest,
|
|
17
|
+
CredentialResponse,
|
|
18
|
+
DocumentListResponseSchema,
|
|
19
|
+
InitiateCallRequest,
|
|
20
|
+
NodeSpec,
|
|
21
|
+
NodeTypesResponse,
|
|
22
|
+
RecordingListResponseSchema,
|
|
23
|
+
ToolResponse,
|
|
24
|
+
UpdateWorkflowRequest,
|
|
25
|
+
WorkflowListResponse,
|
|
26
|
+
WorkflowResponse,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class _GeneratedClient:
|
|
31
|
+
# `ForaClient.__init__` installs `self._request` (see client.py).
|
|
32
|
+
|
|
33
|
+
def create_tool(self, *, body: CreateToolRequest) -> ToolResponse:
|
|
34
|
+
"""Create a reusable tool for the authenticated organization."""
|
|
35
|
+
data = self._request("POST", "/tools/", json=body.model_dump(mode="json", exclude_none=True))
|
|
36
|
+
return ToolResponse.model_validate(data)
|
|
37
|
+
|
|
38
|
+
def create_workflow(self, *, body: CreateWorkflowRequest) -> WorkflowResponse:
|
|
39
|
+
"""Create a new workflow from a workflow definition."""
|
|
40
|
+
data = self._request("POST", "/workflow/create/definition", json=body.model_dump(mode="json", exclude_none=True))
|
|
41
|
+
return WorkflowResponse.model_validate(data)
|
|
42
|
+
|
|
43
|
+
def get_node_type(self, name: str) -> NodeSpec:
|
|
44
|
+
"""Fetch a single node spec by name."""
|
|
45
|
+
data = self._request("GET", f"/node-types/{name}")
|
|
46
|
+
return NodeSpec.model_validate(data)
|
|
47
|
+
|
|
48
|
+
def get_workflow(self, workflow_id: int) -> WorkflowResponse:
|
|
49
|
+
"""Get a single workflow by ID (returns draft if one exists, else published)."""
|
|
50
|
+
data = self._request("GET", f"/workflow/fetch/{workflow_id}")
|
|
51
|
+
return WorkflowResponse.model_validate(data)
|
|
52
|
+
|
|
53
|
+
def list_credentials(self) -> list[CredentialResponse]:
|
|
54
|
+
"""List webhook credentials available to the authenticated organization."""
|
|
55
|
+
data = self._request("GET", "/credentials/")
|
|
56
|
+
return [CredentialResponse.model_validate(x) for x in data]
|
|
57
|
+
|
|
58
|
+
def list_documents(self, *, status: str | None = None, limit: int | None = None, offset: int | None = None) -> DocumentListResponseSchema:
|
|
59
|
+
"""List knowledge base documents available to the authenticated organization."""
|
|
60
|
+
params: dict[str, Any] = {}
|
|
61
|
+
if status is not None:
|
|
62
|
+
params["status"] = status
|
|
63
|
+
if limit is not None:
|
|
64
|
+
params["limit"] = limit
|
|
65
|
+
if offset is not None:
|
|
66
|
+
params["offset"] = offset
|
|
67
|
+
data = self._request("GET", "/knowledge-base/documents", params=params)
|
|
68
|
+
return DocumentListResponseSchema.model_validate(data)
|
|
69
|
+
|
|
70
|
+
def list_node_types(self) -> NodeTypesResponse:
|
|
71
|
+
"""List every registered node type with its spec. Pinned to spec_version."""
|
|
72
|
+
data = self._request("GET", "/node-types")
|
|
73
|
+
return NodeTypesResponse.model_validate(data)
|
|
74
|
+
|
|
75
|
+
def list_recordings(self, *, workflow_id: int | None = None, tts_provider: str | None = None, tts_model: str | None = None, tts_voice_id: str | None = None) -> RecordingListResponseSchema:
|
|
76
|
+
"""List workflow recordings available to the authenticated organization."""
|
|
77
|
+
params: dict[str, Any] = {}
|
|
78
|
+
if workflow_id is not None:
|
|
79
|
+
params["workflow_id"] = workflow_id
|
|
80
|
+
if tts_provider is not None:
|
|
81
|
+
params["tts_provider"] = tts_provider
|
|
82
|
+
if tts_model is not None:
|
|
83
|
+
params["tts_model"] = tts_model
|
|
84
|
+
if tts_voice_id is not None:
|
|
85
|
+
params["tts_voice_id"] = tts_voice_id
|
|
86
|
+
data = self._request("GET", "/workflow-recordings/", params=params)
|
|
87
|
+
return RecordingListResponseSchema.model_validate(data)
|
|
88
|
+
|
|
89
|
+
def list_tools(self, *, status: str | None = None, category: str | None = None) -> list[ToolResponse]:
|
|
90
|
+
"""List tools available to the authenticated organization."""
|
|
91
|
+
params: dict[str, Any] = {}
|
|
92
|
+
if status is not None:
|
|
93
|
+
params["status"] = status
|
|
94
|
+
if category is not None:
|
|
95
|
+
params["category"] = category
|
|
96
|
+
data = self._request("GET", "/tools/", params=params)
|
|
97
|
+
return [ToolResponse.model_validate(x) for x in data]
|
|
98
|
+
|
|
99
|
+
def list_workflows(self, *, status: str | None = None) -> list[WorkflowListResponse]:
|
|
100
|
+
"""List all workflows in the authenticated organization."""
|
|
101
|
+
params: dict[str, Any] = {}
|
|
102
|
+
if status is not None:
|
|
103
|
+
params["status"] = status
|
|
104
|
+
data = self._request("GET", "/workflow/fetch", params=params)
|
|
105
|
+
return [WorkflowListResponse.model_validate(x) for x in data]
|
|
106
|
+
|
|
107
|
+
def test_phone_call(self, *, body: InitiateCallRequest) -> Any:
|
|
108
|
+
"""Place a test call from a workflow to a phone number."""
|
|
109
|
+
return self._request("POST", "/telephony/initiate-call", json=body.model_dump(mode="json", exclude_none=True))
|
|
110
|
+
|
|
111
|
+
def update_workflow(self, workflow_id: int, *, body: UpdateWorkflowRequest) -> WorkflowResponse:
|
|
112
|
+
"""Update a workflow's name and/or definition. Saves as a new draft."""
|
|
113
|
+
data = self._request("PUT", f"/workflow/{workflow_id}", json=body.model_dump(mode="json", exclude_none=True))
|
|
114
|
+
return WorkflowResponse.model_validate(data)
|