Skip to content

Commit 9ee3ac6

Browse files
committed
Release 0.0.0-alpha0
1 parent b6baad4 commit 9ee3ac6

File tree

6 files changed

+74
-41
lines changed

6 files changed

+74
-41
lines changed

README.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Vapi Python Library
22

3-
[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-SDK%20generated%20by%20Fern-brightgreen)](https://github.com/fern-api/fern)
3+
[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=https%3A%2F%2Fgithub.com%2Ffern-demo%2Fvapi-python-sdk)
44
[![pypi](https://img.shields.io/pypi/v/Vapi)](https://pypi.python.org/pypi/Vapi)
55

66
The Vapi Python library provides convenient access to the Vapi API from Python.
@@ -11,6 +11,10 @@ The Vapi Python library provides convenient access to the Vapi API from Python.
1111
pip install Vapi
1212
```
1313

14+
## Reference
15+
16+
A full reference for this library is available [here](./reference.md).
17+
1418
## Usage
1519

1620
Instantiate and use the client with the following:
@@ -95,7 +99,7 @@ A request is deemed retriable when any of the following HTTP status codes is ret
9599
Use the `max_retries` request option to configure this behavior.
96100

97101
```python
98-
client.calls.create(..., {
102+
client.calls.create(..., request_options={
99103
"max_retries": 1
100104
})
101105
```
@@ -115,7 +119,7 @@ client = Vapi(
115119

116120

117121
# Override timeout for a specific method
118-
client.calls.create(..., {
122+
client.calls.create(..., request_options={
119123
"timeout_in_seconds": 1
120124
})
121125
```

poetry.lock

Lines changed: 11 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "Vapi"
3-
version = "0.0.9"
3+
version = "0.0.0-alpha0"
44
description = ""
55
readme = "README.md"
66
authors = []

src/vapi/core/client_wrapper.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ def get_headers(self) -> typing.Dict[str, str]:
2222
headers: typing.Dict[str, str] = {
2323
"X-Fern-Language": "Python",
2424
"X-Fern-SDK-Name": "Vapi",
25-
"X-Fern-SDK-Version": "0.0.9",
25+
"X-Fern-SDK-Version": "0.0.0-alpha0",
2626
}
2727
headers["Authorization"] = f"Bearer {self._get_token()}"
2828
return headers
@@ -52,9 +52,9 @@ def __init__(
5252
super().__init__(token=token, base_url=base_url, timeout=timeout)
5353
self.httpx_client = HttpClient(
5454
httpx_client=httpx_client,
55-
base_headers=self.get_headers(),
56-
base_timeout=self.get_timeout(),
57-
base_url=self.get_base_url(),
55+
base_headers=self.get_headers,
56+
base_timeout=self.get_timeout,
57+
base_url=self.get_base_url,
5858
)
5959

6060

@@ -70,7 +70,7 @@ def __init__(
7070
super().__init__(token=token, base_url=base_url, timeout=timeout)
7171
self.httpx_client = AsyncHttpClient(
7272
httpx_client=httpx_client,
73-
base_headers=self.get_headers(),
74-
base_timeout=self.get_timeout(),
75-
base_url=self.get_base_url(),
73+
base_headers=self.get_headers,
74+
base_timeout=self.get_timeout,
75+
base_url=self.get_base_url,
7676
)

src/vapi/core/http_client.py

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -152,17 +152,20 @@ def __init__(
152152
self,
153153
*,
154154
httpx_client: httpx.Client,
155-
base_timeout: typing.Optional[float],
156-
base_headers: typing.Dict[str, str],
157-
base_url: typing.Optional[str] = None,
155+
base_timeout: typing.Callable[[], typing.Optional[float]],
156+
base_headers: typing.Callable[[], typing.Dict[str, str]],
157+
base_url: typing.Optional[typing.Callable[[], str]] = None,
158158
):
159159
self.base_url = base_url
160160
self.base_timeout = base_timeout
161161
self.base_headers = base_headers
162162
self.httpx_client = httpx_client
163163

164164
def get_base_url(self, maybe_base_url: typing.Optional[str]) -> str:
165-
base_url = self.base_url if maybe_base_url is None else maybe_base_url
165+
base_url = maybe_base_url
166+
if self.base_url is not None and base_url is None:
167+
base_url = self.base_url()
168+
166169
if base_url is None:
167170
raise ValueError("A base_url is required to make this request, please provide one and try again.")
168171
return base_url
@@ -187,7 +190,7 @@ def request(
187190
timeout = (
188191
request_options.get("timeout_in_seconds")
189192
if request_options is not None and request_options.get("timeout_in_seconds") is not None
190-
else self.base_timeout
193+
else self.base_timeout()
191194
)
192195

193196
json_body, data_body = get_request_body(json=json, data=data, request_options=request_options, omit=omit)
@@ -198,7 +201,7 @@ def request(
198201
headers=jsonable_encoder(
199202
remove_none_from_dict(
200203
{
201-
**self.base_headers,
204+
**self.base_headers(),
202205
**(headers if headers is not None else {}),
203206
**(request_options.get("additional_headers", {}) or {} if request_options is not None else {}),
204207
}
@@ -271,7 +274,7 @@ def stream(
271274
timeout = (
272275
request_options.get("timeout_in_seconds")
273276
if request_options is not None and request_options.get("timeout_in_seconds") is not None
274-
else self.base_timeout
277+
else self.base_timeout()
275278
)
276279

277280
json_body, data_body = get_request_body(json=json, data=data, request_options=request_options, omit=omit)
@@ -282,7 +285,7 @@ def stream(
282285
headers=jsonable_encoder(
283286
remove_none_from_dict(
284287
{
285-
**self.base_headers,
288+
**self.base_headers(),
286289
**(headers if headers is not None else {}),
287290
**(request_options.get("additional_headers", {}) if request_options is not None else {}),
288291
}
@@ -321,17 +324,20 @@ def __init__(
321324
self,
322325
*,
323326
httpx_client: httpx.AsyncClient,
324-
base_timeout: typing.Optional[float],
325-
base_headers: typing.Dict[str, str],
326-
base_url: typing.Optional[str] = None,
327+
base_timeout: typing.Callable[[], typing.Optional[float]],
328+
base_headers: typing.Callable[[], typing.Dict[str, str]],
329+
base_url: typing.Optional[typing.Callable[[], str]] = None,
327330
):
328331
self.base_url = base_url
329332
self.base_timeout = base_timeout
330333
self.base_headers = base_headers
331334
self.httpx_client = httpx_client
332335

333336
def get_base_url(self, maybe_base_url: typing.Optional[str]) -> str:
334-
base_url = self.base_url if maybe_base_url is None else maybe_base_url
337+
base_url = maybe_base_url
338+
if self.base_url is not None and base_url is None:
339+
base_url = self.base_url()
340+
335341
if base_url is None:
336342
raise ValueError("A base_url is required to make this request, please provide one and try again.")
337343
return base_url
@@ -356,7 +362,7 @@ async def request(
356362
timeout = (
357363
request_options.get("timeout_in_seconds")
358364
if request_options is not None and request_options.get("timeout_in_seconds") is not None
359-
else self.base_timeout
365+
else self.base_timeout()
360366
)
361367

362368
json_body, data_body = get_request_body(json=json, data=data, request_options=request_options, omit=omit)
@@ -368,7 +374,7 @@ async def request(
368374
headers=jsonable_encoder(
369375
remove_none_from_dict(
370376
{
371-
**self.base_headers,
377+
**self.base_headers(),
372378
**(headers if headers is not None else {}),
373379
**(request_options.get("additional_headers", {}) or {} if request_options is not None else {}),
374380
}
@@ -438,7 +444,7 @@ async def stream(
438444
timeout = (
439445
request_options.get("timeout_in_seconds")
440446
if request_options is not None and request_options.get("timeout_in_seconds") is not None
441-
else self.base_timeout
447+
else self.base_timeout()
442448
)
443449

444450
json_body, data_body = get_request_body(json=json, data=data, request_options=request_options, omit=omit)
@@ -449,7 +455,7 @@ async def stream(
449455
headers=jsonable_encoder(
450456
remove_none_from_dict(
451457
{
452-
**self.base_headers,
458+
**self.base_headers(),
453459
**(headers if headers is not None else {}),
454460
**(request_options.get("additional_headers", {}) if request_options is not None else {}),
455461
}

src/vapi/core/pydantic_utilities.py

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]:
152152
)
153153

154154
else:
155-
_fields_set = self.__fields_set__
155+
_fields_set = self.__fields_set__.copy()
156156

157157
fields = _get_model_fields(self.__class__)
158158
for name, field in fields.items():
@@ -162,9 +162,12 @@ def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]:
162162
# If the default values are non-null act like they've been set
163163
# This effectively allows exclude_unset to work like exclude_none where
164164
# the latter passes through intentionally set none values.
165-
if default != None:
165+
if default is not None or ("exclude_unset" in kwargs and not kwargs["exclude_unset"]):
166166
_fields_set.add(name)
167167

168+
if default is not None:
169+
self.__fields_set__.add(name)
170+
168171
kwargs_with_defaults_exclude_unset_include_fields: typing.Any = {
169172
"by_alias": True,
170173
"exclude_unset": True,
@@ -177,13 +180,33 @@ def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]:
177180
return convert_and_respect_annotation_metadata(object_=dict_dump, annotation=self.__class__, direction="write")
178181

179182

183+
def _union_list_of_pydantic_dicts(
184+
source: typing.List[typing.Any], destination: typing.List[typing.Any]
185+
) -> typing.List[typing.Any]:
186+
converted_list: typing.List[typing.Any] = []
187+
for i, item in enumerate(source):
188+
destination_value = destination[i] # type: ignore
189+
if isinstance(item, dict):
190+
converted_list.append(deep_union_pydantic_dicts(item, destination_value))
191+
elif isinstance(item, list):
192+
converted_list.append(_union_list_of_pydantic_dicts(item, destination_value))
193+
else:
194+
converted_list.append(item)
195+
return converted_list
196+
197+
180198
def deep_union_pydantic_dicts(
181199
source: typing.Dict[str, typing.Any], destination: typing.Dict[str, typing.Any]
182200
) -> typing.Dict[str, typing.Any]:
183201
for key, value in source.items():
202+
node = destination.setdefault(key, {})
184203
if isinstance(value, dict):
185-
node = destination.setdefault(key, {})
186204
deep_union_pydantic_dicts(value, node)
205+
# Note: we do not do this same processing for sets given we do not have sets of models
206+
# and given the sets are unordered, the processing of the set and matching objects would
207+
# be non-trivial.
208+
elif isinstance(value, list):
209+
destination[key] = _union_list_of_pydantic_dicts(value, node)
187210
else:
188211
destination[key] = value
189212

0 commit comments

Comments
 (0)