fathom-python 0.0.24__tar.gz → 0.0.26__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.
Files changed (48) hide show
  1. {fathom_python-0.0.24 → fathom_python-0.0.26}/PKG-INFO +1 -1
  2. {fathom_python-0.0.24 → fathom_python-0.0.26}/pyproject.toml +1 -1
  3. {fathom_python-0.0.24 → fathom_python-0.0.26}/src/fathom/__init__.py +2 -2
  4. {fathom_python-0.0.24 → fathom_python-0.0.26}/src/fathom/base_client.py +91 -73
  5. {fathom_python-0.0.24 → fathom_python-0.0.26}/src/fathom/core/__init__.py +3 -0
  6. {fathom_python-0.0.24 → fathom_python-0.0.26}/src/fathom/core/client_wrapper.py +7 -28
  7. fathom_python-0.0.26/src/fathom/core/pagination.py +82 -0
  8. {fathom_python-0.0.24 → fathom_python-0.0.26}/src/fathom/environment.py +1 -1
  9. {fathom_python-0.0.24 → fathom_python-0.0.26}/src/fathom/raw_base_client.py +105 -25
  10. {fathom_python-0.0.24 → fathom_python-0.0.26}/src/fathom/types/action_item.py +5 -1
  11. {fathom_python-0.0.24 → fathom_python-0.0.26}/src/fathom/types/assignee.py +3 -3
  12. {fathom_python-0.0.24 → fathom_python-0.0.26}/src/fathom/types/fathom_user.py +1 -1
  13. {fathom_python-0.0.24 → fathom_python-0.0.26}/src/fathom/types/invitee.py +2 -2
  14. {fathom_python-0.0.24 → fathom_python-0.0.26}/src/fathom/types/meeting.py +1 -5
  15. {fathom_python-0.0.24 → fathom_python-0.0.26}/src/fathom/types/meeting_list_response.py +1 -1
  16. {fathom_python-0.0.24 → fathom_python-0.0.26}/src/fathom/types/meeting_summary.py +5 -2
  17. {fathom_python-0.0.24 → fathom_python-0.0.26}/src/fathom/types/transcript_item_speaker.py +1 -1
  18. {fathom_python-0.0.24 → fathom_python-0.0.26}/README.md +0 -0
  19. {fathom_python-0.0.24 → fathom_python-0.0.26}/src/fathom/client.py +0 -0
  20. {fathom_python-0.0.24 → fathom_python-0.0.26}/src/fathom/core/api_error.py +0 -0
  21. {fathom_python-0.0.24 → fathom_python-0.0.26}/src/fathom/core/datetime_utils.py +0 -0
  22. {fathom_python-0.0.24 → fathom_python-0.0.26}/src/fathom/core/file.py +0 -0
  23. {fathom_python-0.0.24 → fathom_python-0.0.26}/src/fathom/core/force_multipart.py +0 -0
  24. {fathom_python-0.0.24 → fathom_python-0.0.26}/src/fathom/core/http_client.py +0 -0
  25. {fathom_python-0.0.24 → fathom_python-0.0.26}/src/fathom/core/http_response.py +0 -0
  26. {fathom_python-0.0.24 → fathom_python-0.0.26}/src/fathom/core/jsonable_encoder.py +0 -0
  27. {fathom_python-0.0.24 → fathom_python-0.0.26}/src/fathom/core/pydantic_utilities.py +0 -0
  28. {fathom_python-0.0.24 → fathom_python-0.0.26}/src/fathom/core/query_encoder.py +0 -0
  29. {fathom_python-0.0.24 → fathom_python-0.0.26}/src/fathom/core/remove_none_from_dict.py +0 -0
  30. {fathom_python-0.0.24 → fathom_python-0.0.26}/src/fathom/core/request_options.py +0 -0
  31. {fathom_python-0.0.24 → fathom_python-0.0.26}/src/fathom/core/serialization.py +0 -0
  32. {fathom_python-0.0.24 → fathom_python-0.0.26}/src/fathom/errors/__init__.py +0 -0
  33. {fathom_python-0.0.24 → fathom_python-0.0.26}/src/fathom/errors/bad_request_error.py +0 -0
  34. {fathom_python-0.0.24 → fathom_python-0.0.26}/src/fathom/errors/unauthorized_error.py +0 -0
  35. {fathom_python-0.0.24 → fathom_python-0.0.26}/src/fathom/py.typed +0 -0
  36. {fathom_python-0.0.24 → fathom_python-0.0.26}/src/fathom/types/__init__.py +0 -0
  37. {fathom_python-0.0.24 → fathom_python-0.0.26}/src/fathom/types/crm_company_match.py +0 -0
  38. {fathom_python-0.0.24 → fathom_python-0.0.26}/src/fathom/types/crm_contact_match.py +0 -0
  39. {fathom_python-0.0.24 → fathom_python-0.0.26}/src/fathom/types/crm_deal_match.py +0 -0
  40. {fathom_python-0.0.24 → fathom_python-0.0.26}/src/fathom/types/crm_matches.py +0 -0
  41. {fathom_python-0.0.24 → fathom_python-0.0.26}/src/fathom/types/list_meetings_request_meeting_type.py +0 -0
  42. {fathom_python-0.0.24 → fathom_python-0.0.26}/src/fathom/types/meeting_meeting_type.py +0 -0
  43. {fathom_python-0.0.24 → fathom_python-0.0.26}/src/fathom/types/team.py +0 -0
  44. {fathom_python-0.0.24 → fathom_python-0.0.26}/src/fathom/types/team_list_response.py +0 -0
  45. {fathom_python-0.0.24 → fathom_python-0.0.26}/src/fathom/types/team_member.py +0 -0
  46. {fathom_python-0.0.24 → fathom_python-0.0.26}/src/fathom/types/team_member_list_response.py +0 -0
  47. {fathom_python-0.0.24 → fathom_python-0.0.26}/src/fathom/types/transcript_item.py +0 -0
  48. {fathom_python-0.0.24 → fathom_python-0.0.26}/src/fathom/version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fathom-python
3
- Version: 0.0.24
3
+ Version: 0.0.26
4
4
  Summary:
5
5
  Requires-Python: >=3.8,<4.0
6
6
  Classifier: Intended Audience :: Developers
@@ -3,7 +3,7 @@ name = "fathom-python"
3
3
 
4
4
  [tool.poetry]
5
5
  name = "fathom-python"
6
- version = "0.0.24"
6
+ version = "0.0.26"
7
7
  description = ""
8
8
  readme = "README.md"
9
9
  authors = []
@@ -25,7 +25,7 @@ from .types import (
25
25
  )
26
26
  from .errors import BadRequestError, UnauthorizedError
27
27
  from .client import AsyncFathomApi, FathomApi
28
- from .environment import ClientEnvironment
28
+ from .environment import FathomApiEnvironment
29
29
  from .version import __version__
30
30
 
31
31
  __all__ = [
@@ -34,7 +34,7 @@ __all__ = [
34
34
  "AsyncFathomApi",
35
35
  "BadRequestError",
36
36
  "FathomApi",
37
- "ClientEnvironment",
37
+ "FathomApiEnvironment",
38
38
  "CrmCompanyMatch",
39
39
  "CrmContactMatch",
40
40
  "CrmDealMatch",
@@ -1,18 +1,17 @@
1
1
  # This file was auto-generated by Fern from our API Definition.
2
2
 
3
- import os
4
3
  import typing
5
4
 
6
5
  import httpx
7
- from .core.api_error import ApiError
8
6
  from .core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
7
+ from .core.pagination import AsyncPager, SyncPager
9
8
  from .core.request_options import RequestOptions
10
- from .environment import ClientEnvironment
9
+ from .environment import FathomApiEnvironment
11
10
  from .raw_base_client import AsyncRawBaseClient, RawBaseClient
12
11
  from .types.list_meetings_request_meeting_type import ListMeetingsRequestMeetingType
13
- from .types.meeting_list_response import MeetingListResponse
14
- from .types.team_list_response import TeamListResponse
15
- from .types.team_member_list_response import TeamMemberListResponse
12
+ from .types.meeting import Meeting
13
+ from .types.team import Team
14
+ from .types.team_member import TeamMember
16
15
 
17
16
 
18
17
  class BaseClient:
@@ -24,14 +23,14 @@ class BaseClient:
24
23
  base_url : typing.Optional[str]
25
24
  The base url to use for requests from the client.
26
25
 
27
- environment : ClientEnvironment
28
- The environment to use for requests from the client. from .environment import ClientEnvironment
26
+ environment : FathomApiEnvironment
27
+ The environment to use for requests from the client. from .environment import FathomApiEnvironment
29
28
 
30
- Defaults to ClientEnvironment.DEFAULT
29
+ Defaults to FathomApiEnvironment.DEFAULT
31
30
 
32
31
 
33
32
 
34
- api_key : typing.Optional[typing.Union[str, typing.Callable[[], str]]]
33
+ api_key : str
35
34
  timeout : typing.Optional[float]
36
35
  The timeout to be used, in seconds, for requests. By default the timeout is 60 seconds, unless a custom httpx client is used, in which case this default is not enforced.
37
36
 
@@ -43,16 +42,16 @@ class BaseClient:
43
42
 
44
43
  Examples
45
44
  --------
46
- from fathom import Client
47
- client = Client(api_key="YOUR_API_KEY", )
45
+ from fathom import FathomApi
46
+ client = FathomApi(api_key="YOUR_API_KEY", )
48
47
  """
49
48
 
50
49
  def __init__(
51
50
  self,
52
51
  *,
53
52
  base_url: typing.Optional[str] = None,
54
- environment: ClientEnvironment = ClientEnvironment.DEFAULT,
55
- api_key: typing.Optional[typing.Union[str, typing.Callable[[], str]]] = os.getenv("FATHOM_API_KEY"),
53
+ environment: FathomApiEnvironment = FathomApiEnvironment.DEFAULT,
54
+ api_key: str,
56
55
  timeout: typing.Optional[float] = None,
57
56
  follow_redirects: typing.Optional[bool] = True,
58
57
  httpx_client: typing.Optional[httpx.Client] = None,
@@ -60,10 +59,6 @@ class BaseClient:
60
59
  _defaulted_timeout = (
61
60
  timeout if timeout is not None else 60 if httpx_client is None else httpx_client.timeout.read
62
61
  )
63
- if api_key is None:
64
- raise ApiError(
65
- body="The client must be instantiated be either passing in api_key or setting FATHOM_API_KEY"
66
- )
67
62
  self._client_wrapper = SyncClientWrapper(
68
63
  base_url=_get_base_url(base_url=base_url, environment=environment),
69
64
  api_key=api_key,
@@ -98,7 +93,7 @@ class BaseClient:
98
93
  include_transcript: typing.Optional[bool] = None,
99
94
  cursor: typing.Optional[str] = None,
100
95
  request_options: typing.Optional[RequestOptions] = None,
101
- ) -> MeetingListResponse:
96
+ ) -> SyncPager[Meeting]:
102
97
  """
103
98
  Parameters
104
99
  ----------
@@ -144,16 +139,21 @@ class BaseClient:
144
139
 
145
140
  Returns
146
141
  -------
147
- MeetingListResponse
142
+ SyncPager[Meeting]
148
143
  Paginated list of meetings.
149
144
 
150
145
  Examples
151
146
  --------
152
- from fathom import Client
153
- client = Client(api_key="YOUR_API_KEY", )
154
- client.list_meetings()
147
+ from fathom import FathomApi
148
+ client = FathomApi(api_key="YOUR_API_KEY", )
149
+ response = client.list_meetings()
150
+ for item in response:
151
+ yield item
152
+ # alternatively, you can paginate page-by-page
153
+ for page in response.iter_pages():
154
+ yield page
155
155
  """
156
- _response = self._raw_client.list_meetings(
156
+ return self._raw_client.list_meetings(
157
157
  recorded_by=recorded_by,
158
158
  teams=teams,
159
159
  calendar_invitees=calendar_invitees,
@@ -163,11 +163,10 @@ class BaseClient:
163
163
  cursor=cursor,
164
164
  request_options=request_options,
165
165
  )
166
- return _response.data
167
166
 
168
167
  def list_teams(
169
168
  self, *, cursor: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None
170
- ) -> TeamListResponse:
169
+ ) -> SyncPager[Team]:
171
170
  """
172
171
  Parameters
173
172
  ----------
@@ -179,17 +178,21 @@ class BaseClient:
179
178
 
180
179
  Returns
181
180
  -------
182
- TeamListResponse
181
+ SyncPager[Team]
183
182
  Paginated list of teams.
184
183
 
185
184
  Examples
186
185
  --------
187
- from fathom import Client
188
- client = Client(api_key="YOUR_API_KEY", )
189
- client.list_teams()
186
+ from fathom import FathomApi
187
+ client = FathomApi(api_key="YOUR_API_KEY", )
188
+ response = client.list_teams()
189
+ for item in response:
190
+ yield item
191
+ # alternatively, you can paginate page-by-page
192
+ for page in response.iter_pages():
193
+ yield page
190
194
  """
191
- _response = self._raw_client.list_teams(cursor=cursor, request_options=request_options)
192
- return _response.data
195
+ return self._raw_client.list_teams(cursor=cursor, request_options=request_options)
193
196
 
194
197
  def list_team_members(
195
198
  self,
@@ -197,7 +200,7 @@ class BaseClient:
197
200
  team: typing.Optional[str] = None,
198
201
  cursor: typing.Optional[str] = None,
199
202
  request_options: typing.Optional[RequestOptions] = None,
200
- ) -> TeamMemberListResponse:
203
+ ) -> SyncPager[TeamMember]:
201
204
  """
202
205
  Parameters
203
206
  ----------
@@ -212,17 +215,21 @@ class BaseClient:
212
215
 
213
216
  Returns
214
217
  -------
215
- TeamMemberListResponse
218
+ SyncPager[TeamMember]
216
219
  Paginated list of team members.
217
220
 
218
221
  Examples
219
222
  --------
220
- from fathom import Client
221
- client = Client(api_key="YOUR_API_KEY", )
222
- client.list_team_members()
223
+ from fathom import FathomApi
224
+ client = FathomApi(api_key="YOUR_API_KEY", )
225
+ response = client.list_team_members()
226
+ for item in response:
227
+ yield item
228
+ # alternatively, you can paginate page-by-page
229
+ for page in response.iter_pages():
230
+ yield page
223
231
  """
224
- _response = self._raw_client.list_team_members(team=team, cursor=cursor, request_options=request_options)
225
- return _response.data
232
+ return self._raw_client.list_team_members(team=team, cursor=cursor, request_options=request_options)
226
233
 
227
234
 
228
235
  class AsyncBaseClient:
@@ -234,14 +241,14 @@ class AsyncBaseClient:
234
241
  base_url : typing.Optional[str]
235
242
  The base url to use for requests from the client.
236
243
 
237
- environment : ClientEnvironment
238
- The environment to use for requests from the client. from .environment import ClientEnvironment
244
+ environment : FathomApiEnvironment
245
+ The environment to use for requests from the client. from .environment import FathomApiEnvironment
239
246
 
240
- Defaults to ClientEnvironment.DEFAULT
247
+ Defaults to FathomApiEnvironment.DEFAULT
241
248
 
242
249
 
243
250
 
244
- api_key : typing.Optional[typing.Union[str, typing.Callable[[], str]]]
251
+ api_key : str
245
252
  timeout : typing.Optional[float]
246
253
  The timeout to be used, in seconds, for requests. By default the timeout is 60 seconds, unless a custom httpx client is used, in which case this default is not enforced.
247
254
 
@@ -253,16 +260,16 @@ class AsyncBaseClient:
253
260
 
254
261
  Examples
255
262
  --------
256
- from fathom import AsyncClient
257
- client = AsyncClient(api_key="YOUR_API_KEY", )
263
+ from fathom import AsyncFathomApi
264
+ client = AsyncFathomApi(api_key="YOUR_API_KEY", )
258
265
  """
259
266
 
260
267
  def __init__(
261
268
  self,
262
269
  *,
263
270
  base_url: typing.Optional[str] = None,
264
- environment: ClientEnvironment = ClientEnvironment.DEFAULT,
265
- api_key: typing.Optional[typing.Union[str, typing.Callable[[], str]]] = os.getenv("FATHOM_API_KEY"),
271
+ environment: FathomApiEnvironment = FathomApiEnvironment.DEFAULT,
272
+ api_key: str,
266
273
  timeout: typing.Optional[float] = None,
267
274
  follow_redirects: typing.Optional[bool] = True,
268
275
  httpx_client: typing.Optional[httpx.AsyncClient] = None,
@@ -270,10 +277,6 @@ class AsyncBaseClient:
270
277
  _defaulted_timeout = (
271
278
  timeout if timeout is not None else 60 if httpx_client is None else httpx_client.timeout.read
272
279
  )
273
- if api_key is None:
274
- raise ApiError(
275
- body="The client must be instantiated be either passing in api_key or setting FATHOM_API_KEY"
276
- )
277
280
  self._client_wrapper = AsyncClientWrapper(
278
281
  base_url=_get_base_url(base_url=base_url, environment=environment),
279
282
  api_key=api_key,
@@ -308,7 +311,7 @@ class AsyncBaseClient:
308
311
  include_transcript: typing.Optional[bool] = None,
309
312
  cursor: typing.Optional[str] = None,
310
313
  request_options: typing.Optional[RequestOptions] = None,
311
- ) -> MeetingListResponse:
314
+ ) -> AsyncPager[Meeting]:
312
315
  """
313
316
  Parameters
314
317
  ----------
@@ -354,19 +357,25 @@ class AsyncBaseClient:
354
357
 
355
358
  Returns
356
359
  -------
357
- MeetingListResponse
360
+ AsyncPager[Meeting]
358
361
  Paginated list of meetings.
359
362
 
360
363
  Examples
361
364
  --------
362
- from fathom import AsyncClient
365
+ from fathom import AsyncFathomApi
363
366
  import asyncio
364
- client = AsyncClient(api_key="YOUR_API_KEY", )
367
+ client = AsyncFathomApi(api_key="YOUR_API_KEY", )
365
368
  async def main() -> None:
366
- await client.list_meetings()
369
+ response = await client.list_meetings()
370
+ async for item in response:
371
+ yield item
372
+
373
+ # alternatively, you can paginate page-by-page
374
+ async for page in response.iter_pages():
375
+ yield page
367
376
  asyncio.run(main())
368
377
  """
369
- _response = await self._raw_client.list_meetings(
378
+ return await self._raw_client.list_meetings(
370
379
  recorded_by=recorded_by,
371
380
  teams=teams,
372
381
  calendar_invitees=calendar_invitees,
@@ -376,11 +385,10 @@ class AsyncBaseClient:
376
385
  cursor=cursor,
377
386
  request_options=request_options,
378
387
  )
379
- return _response.data
380
388
 
381
389
  async def list_teams(
382
390
  self, *, cursor: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None
383
- ) -> TeamListResponse:
391
+ ) -> AsyncPager[Team]:
384
392
  """
385
393
  Parameters
386
394
  ----------
@@ -392,20 +400,25 @@ class AsyncBaseClient:
392
400
 
393
401
  Returns
394
402
  -------
395
- TeamListResponse
403
+ AsyncPager[Team]
396
404
  Paginated list of teams.
397
405
 
398
406
  Examples
399
407
  --------
400
- from fathom import AsyncClient
408
+ from fathom import AsyncFathomApi
401
409
  import asyncio
402
- client = AsyncClient(api_key="YOUR_API_KEY", )
410
+ client = AsyncFathomApi(api_key="YOUR_API_KEY", )
403
411
  async def main() -> None:
404
- await client.list_teams()
412
+ response = await client.list_teams()
413
+ async for item in response:
414
+ yield item
415
+
416
+ # alternatively, you can paginate page-by-page
417
+ async for page in response.iter_pages():
418
+ yield page
405
419
  asyncio.run(main())
406
420
  """
407
- _response = await self._raw_client.list_teams(cursor=cursor, request_options=request_options)
408
- return _response.data
421
+ return await self._raw_client.list_teams(cursor=cursor, request_options=request_options)
409
422
 
410
423
  async def list_team_members(
411
424
  self,
@@ -413,7 +426,7 @@ class AsyncBaseClient:
413
426
  team: typing.Optional[str] = None,
414
427
  cursor: typing.Optional[str] = None,
415
428
  request_options: typing.Optional[RequestOptions] = None,
416
- ) -> TeamMemberListResponse:
429
+ ) -> AsyncPager[TeamMember]:
417
430
  """
418
431
  Parameters
419
432
  ----------
@@ -428,23 +441,28 @@ class AsyncBaseClient:
428
441
 
429
442
  Returns
430
443
  -------
431
- TeamMemberListResponse
444
+ AsyncPager[TeamMember]
432
445
  Paginated list of team members.
433
446
 
434
447
  Examples
435
448
  --------
436
- from fathom import AsyncClient
449
+ from fathom import AsyncFathomApi
437
450
  import asyncio
438
- client = AsyncClient(api_key="YOUR_API_KEY", )
451
+ client = AsyncFathomApi(api_key="YOUR_API_KEY", )
439
452
  async def main() -> None:
440
- await client.list_team_members()
453
+ response = await client.list_team_members()
454
+ async for item in response:
455
+ yield item
456
+
457
+ # alternatively, you can paginate page-by-page
458
+ async for page in response.iter_pages():
459
+ yield page
441
460
  asyncio.run(main())
442
461
  """
443
- _response = await self._raw_client.list_team_members(team=team, cursor=cursor, request_options=request_options)
444
- return _response.data
462
+ return await self._raw_client.list_team_members(team=team, cursor=cursor, request_options=request_options)
445
463
 
446
464
 
447
- def _get_base_url(*, base_url: typing.Optional[str] = None, environment: ClientEnvironment) -> str:
465
+ def _get_base_url(*, base_url: typing.Optional[str] = None, environment: FathomApiEnvironment) -> str:
448
466
  if base_url is not None:
449
467
  return base_url
450
468
  elif environment is not None:
@@ -9,6 +9,7 @@ from .file import File, convert_file_dict_to_httpx_tuples, with_content_type
9
9
  from .http_client import AsyncHttpClient, HttpClient
10
10
  from .http_response import AsyncHttpResponse, HttpResponse
11
11
  from .jsonable_encoder import jsonable_encoder
12
+ from .pagination import AsyncPager, SyncPager
12
13
  from .pydantic_utilities import (
13
14
  IS_PYDANTIC_V2,
14
15
  UniversalBaseModel,
@@ -28,6 +29,7 @@ __all__ = [
28
29
  "AsyncClientWrapper",
29
30
  "AsyncHttpClient",
30
31
  "AsyncHttpResponse",
32
+ "AsyncPager",
31
33
  "BaseClientWrapper",
32
34
  "FieldMetadata",
33
35
  "File",
@@ -36,6 +38,7 @@ __all__ = [
36
38
  "IS_PYDANTIC_V2",
37
39
  "RequestOptions",
38
40
  "SyncClientWrapper",
41
+ "SyncPager",
39
42
  "UniversalBaseModel",
40
43
  "UniversalRootModel",
41
44
  "convert_and_respect_annotation_metadata",
@@ -7,32 +7,21 @@ from .http_client import AsyncHttpClient, HttpClient
7
7
 
8
8
 
9
9
  class BaseClientWrapper:
10
- def __init__(
11
- self,
12
- *,
13
- api_key: typing.Union[str, typing.Callable[[], str]],
14
- base_url: str,
15
- timeout: typing.Optional[float] = None,
16
- ):
17
- self._api_key = api_key
10
+ def __init__(self, *, api_key: str, base_url: str, timeout: typing.Optional[float] = None):
11
+ self.api_key = api_key
18
12
  self._base_url = base_url
19
13
  self._timeout = timeout
20
14
 
21
15
  def get_headers(self) -> typing.Dict[str, str]:
22
16
  headers: typing.Dict[str, str] = {
17
+ "User-Agent": "fathom-python/0.0.26",
23
18
  "X-Fern-Language": "Python",
24
19
  "X-Fern-SDK-Name": "fathom-python",
25
- "X-Fern-SDK-Version": "0.0.24",
20
+ "X-Fern-SDK-Version": "0.0.26",
26
21
  }
27
- headers["Authorization"] = f"Bearer {self._get_api_key()}"
22
+ headers["X-Api-Key"] = self.api_key
28
23
  return headers
29
24
 
30
- def _get_api_key(self) -> str:
31
- if isinstance(self._api_key, str):
32
- return self._api_key
33
- else:
34
- return self._api_key()
35
-
36
25
  def get_base_url(self) -> str:
37
26
  return self._base_url
38
27
 
@@ -42,12 +31,7 @@ class BaseClientWrapper:
42
31
 
43
32
  class SyncClientWrapper(BaseClientWrapper):
44
33
  def __init__(
45
- self,
46
- *,
47
- api_key: typing.Union[str, typing.Callable[[], str]],
48
- base_url: str,
49
- timeout: typing.Optional[float] = None,
50
- httpx_client: httpx.Client,
34
+ self, *, api_key: str, base_url: str, timeout: typing.Optional[float] = None, httpx_client: httpx.Client
51
35
  ):
52
36
  super().__init__(api_key=api_key, base_url=base_url, timeout=timeout)
53
37
  self.httpx_client = HttpClient(
@@ -60,12 +44,7 @@ class SyncClientWrapper(BaseClientWrapper):
60
44
 
61
45
  class AsyncClientWrapper(BaseClientWrapper):
62
46
  def __init__(
63
- self,
64
- *,
65
- api_key: typing.Union[str, typing.Callable[[], str]],
66
- base_url: str,
67
- timeout: typing.Optional[float] = None,
68
- httpx_client: httpx.AsyncClient,
47
+ self, *, api_key: str, base_url: str, timeout: typing.Optional[float] = None, httpx_client: httpx.AsyncClient
69
48
  ):
70
49
  super().__init__(api_key=api_key, base_url=base_url, timeout=timeout)
71
50
  self.httpx_client = AsyncHttpClient(
@@ -0,0 +1,82 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+ from typing import AsyncIterator, Awaitable, Callable, Generic, Iterator, List, Optional, TypeVar
7
+
8
+ from .http_response import BaseHttpResponse
9
+
10
+ T = TypeVar("T")
11
+ """Generic to represent the underlying type of the results within a page"""
12
+
13
+
14
+ # SDKs implement a Page ABC per-pagination request, the endpoint then returns a pager that wraps this type
15
+ # for example, an endpoint will return SyncPager[UserPage] where UserPage implements the Page ABC. ex:
16
+ #
17
+ # SyncPager<InnerListType>(
18
+ # has_next=response.list_metadata.after is not None,
19
+ # items=response.data,
20
+ # # This should be the outer function that returns the SyncPager again
21
+ # get_next=lambda: list(..., cursor: response.cursor) (or list(..., offset: offset + 1))
22
+ # )
23
+
24
+
25
+ @dataclass(frozen=True)
26
+ class SyncPager(Generic[T]):
27
+ get_next: Optional[Callable[[], Optional[SyncPager[T]]]]
28
+ has_next: bool
29
+ items: Optional[List[T]]
30
+ response: Optional[BaseHttpResponse]
31
+
32
+ # Here we type ignore the iterator to avoid a mypy error
33
+ # caused by the type conflict with Pydanitc's __iter__ method
34
+ # brought in by extending the base model
35
+ def __iter__(self) -> Iterator[T]: # type: ignore[override]
36
+ for page in self.iter_pages():
37
+ if page.items is not None:
38
+ yield from page.items
39
+
40
+ def iter_pages(self) -> Iterator[SyncPager[T]]:
41
+ page: Optional[SyncPager[T]] = self
42
+ while page is not None:
43
+ yield page
44
+
45
+ if not page.has_next or page.get_next is None:
46
+ return
47
+
48
+ page = page.get_next()
49
+ if page is None or page.items is None or len(page.items) == 0:
50
+ return
51
+
52
+ def next_page(self) -> Optional[SyncPager[T]]:
53
+ return self.get_next() if self.get_next is not None else None
54
+
55
+
56
+ @dataclass(frozen=True)
57
+ class AsyncPager(Generic[T]):
58
+ get_next: Optional[Callable[[], Awaitable[Optional[AsyncPager[T]]]]]
59
+ has_next: bool
60
+ items: Optional[List[T]]
61
+ response: Optional[BaseHttpResponse]
62
+
63
+ async def __aiter__(self) -> AsyncIterator[T]:
64
+ async for page in self.iter_pages():
65
+ if page.items is not None:
66
+ for item in page.items:
67
+ yield item
68
+
69
+ async def iter_pages(self) -> AsyncIterator[AsyncPager[T]]:
70
+ page: Optional[AsyncPager[T]] = self
71
+ while page is not None:
72
+ yield page
73
+
74
+ if not page.has_next or page.get_next is None:
75
+ return
76
+
77
+ page = await page.get_next()
78
+ if page is None or page.items is None or len(page.items) == 0:
79
+ return
80
+
81
+ async def next_page(self) -> Optional[AsyncPager[T]]:
82
+ return await self.get_next() if self.get_next is not None else None
@@ -3,5 +3,5 @@
3
3
  import enum
4
4
 
5
5
 
6
- class ClientEnvironment(enum.Enum):
6
+ class FathomApiEnvironment(enum.Enum):
7
7
  DEFAULT = "https://api.fathom.ai/external/v1"
@@ -5,14 +5,17 @@ from json.decoder import JSONDecodeError
5
5
 
6
6
  from .core.api_error import ApiError
7
7
  from .core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
8
- from .core.http_response import AsyncHttpResponse, HttpResponse
8
+ from .core.pagination import AsyncPager, BaseHttpResponse, SyncPager
9
9
  from .core.pydantic_utilities import parse_obj_as
10
10
  from .core.request_options import RequestOptions
11
11
  from .errors.bad_request_error import BadRequestError
12
12
  from .errors.unauthorized_error import UnauthorizedError
13
13
  from .types.list_meetings_request_meeting_type import ListMeetingsRequestMeetingType
14
+ from .types.meeting import Meeting
14
15
  from .types.meeting_list_response import MeetingListResponse
16
+ from .types.team import Team
15
17
  from .types.team_list_response import TeamListResponse
18
+ from .types.team_member import TeamMember
16
19
  from .types.team_member_list_response import TeamMemberListResponse
17
20
 
18
21
 
@@ -31,7 +34,7 @@ class RawBaseClient:
31
34
  include_transcript: typing.Optional[bool] = None,
32
35
  cursor: typing.Optional[str] = None,
33
36
  request_options: typing.Optional[RequestOptions] = None,
34
- ) -> HttpResponse[MeetingListResponse]:
37
+ ) -> SyncPager[Meeting]:
35
38
  """
36
39
  Parameters
37
40
  ----------
@@ -77,7 +80,7 @@ class RawBaseClient:
77
80
 
78
81
  Returns
79
82
  -------
80
- HttpResponse[MeetingListResponse]
83
+ SyncPager[Meeting]
81
84
  Paginated list of meetings.
82
85
  """
83
86
  _response = self._client_wrapper.httpx_client.request(
@@ -96,14 +99,29 @@ class RawBaseClient:
96
99
  )
97
100
  try:
98
101
  if 200 <= _response.status_code < 300:
99
- _data = typing.cast(
102
+ _parsed_response = typing.cast(
100
103
  MeetingListResponse,
101
104
  parse_obj_as(
102
105
  type_=MeetingListResponse, # type: ignore
103
106
  object_=_response.json(),
104
107
  ),
105
108
  )
106
- return HttpResponse(response=_response, data=_data)
109
+ _items = _parsed_response.items
110
+ _parsed_next = _parsed_response.next_cursor
111
+ _has_next = _parsed_next is not None and _parsed_next != ""
112
+ _get_next = lambda: self.list_meetings(
113
+ recorded_by=recorded_by,
114
+ teams=teams,
115
+ calendar_invitees=calendar_invitees,
116
+ created_after=created_after,
117
+ meeting_type=meeting_type,
118
+ include_transcript=include_transcript,
119
+ cursor=_parsed_next,
120
+ request_options=request_options,
121
+ )
122
+ return SyncPager(
123
+ has_next=_has_next, items=_items, get_next=_get_next, response=BaseHttpResponse(response=_response)
124
+ )
107
125
  if _response.status_code == 400:
108
126
  raise BadRequestError(
109
127
  headers=dict(_response.headers),
@@ -133,7 +151,7 @@ class RawBaseClient:
133
151
 
134
152
  def list_teams(
135
153
  self, *, cursor: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None
136
- ) -> HttpResponse[TeamListResponse]:
154
+ ) -> SyncPager[Team]:
137
155
  """
138
156
  Parameters
139
157
  ----------
@@ -145,7 +163,7 @@ class RawBaseClient:
145
163
 
146
164
  Returns
147
165
  -------
148
- HttpResponse[TeamListResponse]
166
+ SyncPager[Team]
149
167
  Paginated list of teams.
150
168
  """
151
169
  _response = self._client_wrapper.httpx_client.request(
@@ -158,14 +176,23 @@ class RawBaseClient:
158
176
  )
159
177
  try:
160
178
  if 200 <= _response.status_code < 300:
161
- _data = typing.cast(
179
+ _parsed_response = typing.cast(
162
180
  TeamListResponse,
163
181
  parse_obj_as(
164
182
  type_=TeamListResponse, # type: ignore
165
183
  object_=_response.json(),
166
184
  ),
167
185
  )
168
- return HttpResponse(response=_response, data=_data)
186
+ _items = _parsed_response.items
187
+ _parsed_next = _parsed_response.next_cursor
188
+ _has_next = _parsed_next is not None and _parsed_next != ""
189
+ _get_next = lambda: self.list_teams(
190
+ cursor=_parsed_next,
191
+ request_options=request_options,
192
+ )
193
+ return SyncPager(
194
+ has_next=_has_next, items=_items, get_next=_get_next, response=BaseHttpResponse(response=_response)
195
+ )
169
196
  if _response.status_code == 400:
170
197
  raise BadRequestError(
171
198
  headers=dict(_response.headers),
@@ -199,7 +226,7 @@ class RawBaseClient:
199
226
  team: typing.Optional[str] = None,
200
227
  cursor: typing.Optional[str] = None,
201
228
  request_options: typing.Optional[RequestOptions] = None,
202
- ) -> HttpResponse[TeamMemberListResponse]:
229
+ ) -> SyncPager[TeamMember]:
203
230
  """
204
231
  Parameters
205
232
  ----------
@@ -214,7 +241,7 @@ class RawBaseClient:
214
241
 
215
242
  Returns
216
243
  -------
217
- HttpResponse[TeamMemberListResponse]
244
+ SyncPager[TeamMember]
218
245
  Paginated list of team members.
219
246
  """
220
247
  _response = self._client_wrapper.httpx_client.request(
@@ -228,14 +255,24 @@ class RawBaseClient:
228
255
  )
229
256
  try:
230
257
  if 200 <= _response.status_code < 300:
231
- _data = typing.cast(
258
+ _parsed_response = typing.cast(
232
259
  TeamMemberListResponse,
233
260
  parse_obj_as(
234
261
  type_=TeamMemberListResponse, # type: ignore
235
262
  object_=_response.json(),
236
263
  ),
237
264
  )
238
- return HttpResponse(response=_response, data=_data)
265
+ _items = _parsed_response.items
266
+ _parsed_next = _parsed_response.next_cursor
267
+ _has_next = _parsed_next is not None and _parsed_next != ""
268
+ _get_next = lambda: self.list_team_members(
269
+ team=team,
270
+ cursor=_parsed_next,
271
+ request_options=request_options,
272
+ )
273
+ return SyncPager(
274
+ has_next=_has_next, items=_items, get_next=_get_next, response=BaseHttpResponse(response=_response)
275
+ )
239
276
  if _response.status_code == 400:
240
277
  raise BadRequestError(
241
278
  headers=dict(_response.headers),
@@ -279,7 +316,7 @@ class AsyncRawBaseClient:
279
316
  include_transcript: typing.Optional[bool] = None,
280
317
  cursor: typing.Optional[str] = None,
281
318
  request_options: typing.Optional[RequestOptions] = None,
282
- ) -> AsyncHttpResponse[MeetingListResponse]:
319
+ ) -> AsyncPager[Meeting]:
283
320
  """
284
321
  Parameters
285
322
  ----------
@@ -325,7 +362,7 @@ class AsyncRawBaseClient:
325
362
 
326
363
  Returns
327
364
  -------
328
- AsyncHttpResponse[MeetingListResponse]
365
+ AsyncPager[Meeting]
329
366
  Paginated list of meetings.
330
367
  """
331
368
  _response = await self._client_wrapper.httpx_client.request(
@@ -344,14 +381,32 @@ class AsyncRawBaseClient:
344
381
  )
345
382
  try:
346
383
  if 200 <= _response.status_code < 300:
347
- _data = typing.cast(
384
+ _parsed_response = typing.cast(
348
385
  MeetingListResponse,
349
386
  parse_obj_as(
350
387
  type_=MeetingListResponse, # type: ignore
351
388
  object_=_response.json(),
352
389
  ),
353
390
  )
354
- return AsyncHttpResponse(response=_response, data=_data)
391
+ _items = _parsed_response.items
392
+ _parsed_next = _parsed_response.next_cursor
393
+ _has_next = _parsed_next is not None and _parsed_next != ""
394
+
395
+ async def _get_next():
396
+ return await self.list_meetings(
397
+ recorded_by=recorded_by,
398
+ teams=teams,
399
+ calendar_invitees=calendar_invitees,
400
+ created_after=created_after,
401
+ meeting_type=meeting_type,
402
+ include_transcript=include_transcript,
403
+ cursor=_parsed_next,
404
+ request_options=request_options,
405
+ )
406
+
407
+ return AsyncPager(
408
+ has_next=_has_next, items=_items, get_next=_get_next, response=BaseHttpResponse(response=_response)
409
+ )
355
410
  if _response.status_code == 400:
356
411
  raise BadRequestError(
357
412
  headers=dict(_response.headers),
@@ -381,7 +436,7 @@ class AsyncRawBaseClient:
381
436
 
382
437
  async def list_teams(
383
438
  self, *, cursor: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None
384
- ) -> AsyncHttpResponse[TeamListResponse]:
439
+ ) -> AsyncPager[Team]:
385
440
  """
386
441
  Parameters
387
442
  ----------
@@ -393,7 +448,7 @@ class AsyncRawBaseClient:
393
448
 
394
449
  Returns
395
450
  -------
396
- AsyncHttpResponse[TeamListResponse]
451
+ AsyncPager[Team]
397
452
  Paginated list of teams.
398
453
  """
399
454
  _response = await self._client_wrapper.httpx_client.request(
@@ -406,14 +461,26 @@ class AsyncRawBaseClient:
406
461
  )
407
462
  try:
408
463
  if 200 <= _response.status_code < 300:
409
- _data = typing.cast(
464
+ _parsed_response = typing.cast(
410
465
  TeamListResponse,
411
466
  parse_obj_as(
412
467
  type_=TeamListResponse, # type: ignore
413
468
  object_=_response.json(),
414
469
  ),
415
470
  )
416
- return AsyncHttpResponse(response=_response, data=_data)
471
+ _items = _parsed_response.items
472
+ _parsed_next = _parsed_response.next_cursor
473
+ _has_next = _parsed_next is not None and _parsed_next != ""
474
+
475
+ async def _get_next():
476
+ return await self.list_teams(
477
+ cursor=_parsed_next,
478
+ request_options=request_options,
479
+ )
480
+
481
+ return AsyncPager(
482
+ has_next=_has_next, items=_items, get_next=_get_next, response=BaseHttpResponse(response=_response)
483
+ )
417
484
  if _response.status_code == 400:
418
485
  raise BadRequestError(
419
486
  headers=dict(_response.headers),
@@ -447,7 +514,7 @@ class AsyncRawBaseClient:
447
514
  team: typing.Optional[str] = None,
448
515
  cursor: typing.Optional[str] = None,
449
516
  request_options: typing.Optional[RequestOptions] = None,
450
- ) -> AsyncHttpResponse[TeamMemberListResponse]:
517
+ ) -> AsyncPager[TeamMember]:
451
518
  """
452
519
  Parameters
453
520
  ----------
@@ -462,7 +529,7 @@ class AsyncRawBaseClient:
462
529
 
463
530
  Returns
464
531
  -------
465
- AsyncHttpResponse[TeamMemberListResponse]
532
+ AsyncPager[TeamMember]
466
533
  Paginated list of team members.
467
534
  """
468
535
  _response = await self._client_wrapper.httpx_client.request(
@@ -476,14 +543,27 @@ class AsyncRawBaseClient:
476
543
  )
477
544
  try:
478
545
  if 200 <= _response.status_code < 300:
479
- _data = typing.cast(
546
+ _parsed_response = typing.cast(
480
547
  TeamMemberListResponse,
481
548
  parse_obj_as(
482
549
  type_=TeamMemberListResponse, # type: ignore
483
550
  object_=_response.json(),
484
551
  ),
485
552
  )
486
- return AsyncHttpResponse(response=_response, data=_data)
553
+ _items = _parsed_response.items
554
+ _parsed_next = _parsed_response.next_cursor
555
+ _has_next = _parsed_next is not None and _parsed_next != ""
556
+
557
+ async def _get_next():
558
+ return await self.list_team_members(
559
+ team=team,
560
+ cursor=_parsed_next,
561
+ request_options=request_options,
562
+ )
563
+
564
+ return AsyncPager(
565
+ has_next=_has_next, items=_items, get_next=_get_next, response=BaseHttpResponse(response=_response)
566
+ )
487
567
  if _response.status_code == 400:
488
568
  raise BadRequestError(
489
569
  headers=dict(_response.headers),
@@ -8,7 +8,11 @@ from .assignee import Assignee
8
8
 
9
9
 
10
10
  class ActionItem(UniversalBaseModel):
11
- description: str
11
+ description: str = pydantic.Field()
12
+ """
13
+ Always displayed in English.
14
+ """
15
+
12
16
  user_generated: bool
13
17
  completed: bool
14
18
  recording_timestamp: str = pydantic.Field()
@@ -7,9 +7,9 @@ from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
7
7
 
8
8
 
9
9
  class Assignee(UniversalBaseModel):
10
- name: str
11
- email: str
12
- team: str
10
+ name: typing.Optional[str] = None
11
+ email: typing.Optional[str] = None
12
+ team: typing.Optional[str] = None
13
13
 
14
14
  if IS_PYDANTIC_V2:
15
15
  model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
@@ -9,7 +9,7 @@ from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
9
9
  class FathomUser(UniversalBaseModel):
10
10
  name: str
11
11
  email: str
12
- team: str
12
+ team: typing.Optional[str] = None
13
13
 
14
14
  if IS_PYDANTIC_V2:
15
15
  model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
@@ -7,9 +7,9 @@ from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
7
7
 
8
8
 
9
9
  class Invitee(UniversalBaseModel):
10
- name: str
10
+ name: typing.Optional[str] = None
11
11
  matched_speaker_display_name: typing.Optional[str] = None
12
- email: str
12
+ email: typing.Optional[str] = None
13
13
  is_external: bool
14
14
 
15
15
  if IS_PYDANTIC_V2:
@@ -21,11 +21,6 @@ class Meeting(UniversalBaseModel):
21
21
  Calendar event title.
22
22
  """
23
23
 
24
- abstract: str = pydantic.Field()
25
- """
26
- Short meeting summary.
27
- """
28
-
29
24
  url: str
30
25
  share_url: str
31
26
  created_at: dt.datetime
@@ -38,6 +33,7 @@ class Meeting(UniversalBaseModel):
38
33
  summary: typing.Optional[MeetingSummary] = None
39
34
  action_items: typing.Optional[typing.List[ActionItem]] = None
40
35
  calendar_invitees: typing.List[Invitee]
36
+ transcript_language: str
41
37
  recorded_by: FathomUser
42
38
  crm_matches: typing.Optional[CrmMatches] = None
43
39
 
@@ -8,7 +8,7 @@ from .meeting import Meeting
8
8
 
9
9
 
10
10
  class MeetingListResponse(UniversalBaseModel):
11
- limit: int
11
+ limit: typing.Optional[int] = None
12
12
  next_cursor: typing.Optional[str] = None
13
13
  items: typing.List[Meeting]
14
14
 
@@ -7,8 +7,11 @@ from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
7
7
 
8
8
 
9
9
  class MeetingSummary(UniversalBaseModel):
10
- template_name: str
11
- markdown_formatted: str
10
+ template_name: typing.Optional[str] = None
11
+ markdown_formatted: typing.Optional[str] = pydantic.Field(default=None)
12
+ """
13
+ Always displayed in English.
14
+ """
12
15
 
13
16
  if IS_PYDANTIC_V2:
14
17
  model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
@@ -8,7 +8,7 @@ from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
8
8
 
9
9
  class TranscriptItemSpeaker(UniversalBaseModel):
10
10
  display_name: str
11
- matched_calendar_invitee_email: str
11
+ matched_calendar_invitee_email: typing.Optional[str] = None
12
12
 
13
13
  if IS_PYDANTIC_V2:
14
14
  model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
File without changes