seekrai 0.0.1__py3-none-any.whl

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 (56) hide show
  1. seekrai/__init__.py +64 -0
  2. seekrai/abstract/__init__.py +1 -0
  3. seekrai/abstract/api_requestor.py +710 -0
  4. seekrai/cli/__init__.py +0 -0
  5. seekrai/cli/api/__init__.py +0 -0
  6. seekrai/cli/api/chat.py +245 -0
  7. seekrai/cli/api/completions.py +107 -0
  8. seekrai/cli/api/files.py +125 -0
  9. seekrai/cli/api/finetune.py +175 -0
  10. seekrai/cli/api/images.py +82 -0
  11. seekrai/cli/api/models.py +42 -0
  12. seekrai/cli/cli.py +77 -0
  13. seekrai/client.py +154 -0
  14. seekrai/constants.py +32 -0
  15. seekrai/error.py +188 -0
  16. seekrai/filemanager.py +393 -0
  17. seekrai/legacy/__init__.py +0 -0
  18. seekrai/legacy/base.py +27 -0
  19. seekrai/legacy/complete.py +91 -0
  20. seekrai/legacy/embeddings.py +25 -0
  21. seekrai/legacy/files.py +140 -0
  22. seekrai/legacy/finetune.py +173 -0
  23. seekrai/legacy/images.py +25 -0
  24. seekrai/legacy/models.py +44 -0
  25. seekrai/resources/__init__.py +25 -0
  26. seekrai/resources/chat/__init__.py +24 -0
  27. seekrai/resources/chat/completions.py +241 -0
  28. seekrai/resources/completions.py +205 -0
  29. seekrai/resources/embeddings.py +100 -0
  30. seekrai/resources/files.py +173 -0
  31. seekrai/resources/finetune.py +425 -0
  32. seekrai/resources/images.py +156 -0
  33. seekrai/resources/models.py +75 -0
  34. seekrai/seekrflow_response.py +50 -0
  35. seekrai/types/__init__.py +67 -0
  36. seekrai/types/abstract.py +26 -0
  37. seekrai/types/chat_completions.py +151 -0
  38. seekrai/types/common.py +64 -0
  39. seekrai/types/completions.py +86 -0
  40. seekrai/types/embeddings.py +35 -0
  41. seekrai/types/error.py +16 -0
  42. seekrai/types/files.py +88 -0
  43. seekrai/types/finetune.py +218 -0
  44. seekrai/types/images.py +42 -0
  45. seekrai/types/models.py +43 -0
  46. seekrai/utils/__init__.py +28 -0
  47. seekrai/utils/_log.py +61 -0
  48. seekrai/utils/api_helpers.py +84 -0
  49. seekrai/utils/files.py +204 -0
  50. seekrai/utils/tools.py +75 -0
  51. seekrai/version.py +6 -0
  52. seekrai-0.0.1.dist-info/LICENSE +201 -0
  53. seekrai-0.0.1.dist-info/METADATA +401 -0
  54. seekrai-0.0.1.dist-info/RECORD +56 -0
  55. seekrai-0.0.1.dist-info/WHEEL +4 -0
  56. seekrai-0.0.1.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,425 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+
5
+ from seekrai.abstract import api_requestor
6
+ from seekrai.filemanager import DownloadManager
7
+ from seekrai.seekrflow_response import SeekrFlowResponse
8
+ from seekrai.types import (
9
+ FinetuneDownloadResult,
10
+ FinetuneList,
11
+ FinetuneListEvents,
12
+ FinetuneRequest,
13
+ FinetuneResponse,
14
+ SeekrFlowClient,
15
+ SeekrFlowRequest,
16
+ TrainingConfig,
17
+ InfrastructureConfig
18
+ )
19
+ from seekrai.utils import normalize_key
20
+
21
+
22
+ class FineTuning:
23
+ def __init__(self, client: SeekrFlowClient) -> None:
24
+ self._client = client
25
+
26
+ def create(
27
+ self,
28
+ *,
29
+ training_config: TrainingConfig,
30
+ infrastructure_config: InfrastructureConfig
31
+ # wandb_api_key: str | None = None,
32
+ ) -> FinetuneResponse:
33
+ """
34
+ Method to initiate a fine-tuning job
35
+
36
+ Args:
37
+ training_file (str): File-ID of a file uploaded to the SeekrFlow API
38
+ model (str): Name of the base model to run fine-tune job on
39
+ n_epochs (int, optional): Number of epochs for fine-tuning. Defaults to 1.
40
+ n_checkpoints (int, optional): Number of checkpoints to save during fine-tuning.
41
+ Defaults to 1.
42
+ batch_size (int, optional): Batch size for fine-tuning. Defaults to 32.
43
+ learning_rate (float, optional): Learning rate multiplier to use for training
44
+ Defaults to 0.00001.
45
+ suffix (str, optional): Up to 40 character suffix that will be added to your fine-tuned model name.
46
+ Defaults to None.
47
+ wandb_api_key (str, optional): API key for Weights & Biases integration.
48
+ Defaults to None.
49
+
50
+ Returns:
51
+ FinetuneResponse: Object containing information about fine-tuning job.
52
+ """
53
+
54
+ requestor = api_requestor.APIRequestor(
55
+ client=self._client,
56
+ )
57
+
58
+ parameter_payload = FinetuneRequest(
59
+ training_config=training_config,
60
+ infrastructure_config=infrastructure_config
61
+ ).model_dump()
62
+
63
+ response, _, _ = requestor.request(
64
+ options=SeekrFlowRequest(
65
+ method="POST",
66
+ url="fine-tune",
67
+ params=parameter_payload,
68
+ ),
69
+ stream=False,
70
+ )
71
+
72
+ assert isinstance(response, SeekrFlowResponse)
73
+
74
+ return FinetuneResponse(**response.data)
75
+
76
+ def list(self) -> FinetuneList:
77
+ """
78
+ Lists fine-tune job history
79
+
80
+ Returns:
81
+ FinetuneList: Object containing a list of fine-tune jobs
82
+ """
83
+
84
+ requestor = api_requestor.APIRequestor(
85
+ client=self._client,
86
+ )
87
+
88
+ response, _, _ = requestor.request(
89
+ options=SeekrFlowRequest(
90
+ method="GET",
91
+ url="fine-tunes",
92
+ ),
93
+ stream=False,
94
+ )
95
+
96
+ assert isinstance(response, SeekrFlowResponse)
97
+
98
+ return FinetuneList(**response.data)
99
+
100
+ def retrieve(self, id: str) -> FinetuneResponse:
101
+ """
102
+ Retrieves fine-tune job details
103
+
104
+ Args:
105
+ id (str): Fine-tune ID to retrieve. A string that starts with `ft-`.
106
+
107
+ Returns:
108
+ FinetuneResponse: Object containing information about fine-tuning job.
109
+ """
110
+
111
+ requestor = api_requestor.APIRequestor(
112
+ client=self._client,
113
+ )
114
+
115
+ response, _, _ = requestor.request(
116
+ options=SeekrFlowRequest(
117
+ method="GET",
118
+ url=f"fine-tunes/{id}",
119
+ ),
120
+ stream=False,
121
+ )
122
+
123
+ assert isinstance(response, SeekrFlowResponse)
124
+
125
+ return FinetuneResponse(**response.data)
126
+
127
+ def cancel(self, id: str) -> FinetuneResponse:
128
+ """
129
+ Method to cancel a running fine-tuning job
130
+
131
+ Args:
132
+ id (str): Fine-tune ID to cancel. A string that starts with `ft-`.
133
+
134
+ Returns:
135
+ FinetuneResponse: Object containing information about cancelled fine-tuning job.
136
+ """
137
+
138
+ requestor = api_requestor.APIRequestor(
139
+ client=self._client,
140
+ )
141
+
142
+ response, _, _ = requestor.request(
143
+ options=SeekrFlowRequest(
144
+ method="POST",
145
+ url=f"fine-tunes/{id}/cancel",
146
+ ),
147
+ stream=False,
148
+ )
149
+
150
+ assert isinstance(response, SeekrFlowResponse)
151
+
152
+ return FinetuneResponse(**response.data)
153
+
154
+ def list_events(self, id: str) -> FinetuneListEvents:
155
+ """
156
+ Lists events of a fine-tune job
157
+
158
+ Args:
159
+ id (str): Fine-tune ID to list events for. A string that starts with `ft-`.
160
+
161
+ Returns:
162
+ FinetuneListEvents: Object containing list of fine-tune events
163
+ """
164
+
165
+ requestor = api_requestor.APIRequestor(
166
+ client=self._client,
167
+ )
168
+
169
+ response, _, _ = requestor.request(
170
+ options=SeekrFlowRequest(
171
+ method="GET",
172
+ url=f"fine-tunes/{id}/events",
173
+ ),
174
+ stream=False,
175
+ )
176
+
177
+ assert isinstance(response, SeekrFlowResponse)
178
+
179
+ return FinetuneListEvents(**response.data)
180
+
181
+ def download(
182
+ self, id: str, *, output: Path | str | None = None, checkpoint_step: int = -1
183
+ ) -> FinetuneDownloadResult:
184
+ """
185
+ Downloads compressed fine-tuned model or checkpoint to local disk.
186
+
187
+ Defaults file location to `$PWD/{model_name}.{extension}`
188
+
189
+ Args:
190
+ id (str): Fine-tune ID to download. A string that starts with `ft-`.
191
+ output (pathlib.Path | str, optional): Specifies output file name for downloaded model.
192
+ Defaults to None.
193
+ checkpoint_step (int, optional): Specifies step number for checkpoint to download.
194
+ Defaults to -1 (download the final model)
195
+
196
+ Returns:
197
+ FinetuneDownloadResult: Object containing downloaded model metadata
198
+ """
199
+
200
+ url = f"finetune/download?ft_id={id}"
201
+
202
+ if checkpoint_step > 0:
203
+ url += f"&checkpoint_step={checkpoint_step}"
204
+
205
+ remote_name = self.retrieve(id).output_name
206
+
207
+ download_manager = DownloadManager(self._client)
208
+
209
+ if isinstance(output, str):
210
+ output = Path(output)
211
+
212
+ downloaded_filename, file_size = download_manager.download(
213
+ url, output, normalize_key(remote_name or id), fetch_metadata=True
214
+ )
215
+
216
+ return FinetuneDownloadResult(
217
+ object="local",
218
+ id=id,
219
+ checkpoint_step=checkpoint_step,
220
+ filename=downloaded_filename,
221
+ size=file_size,
222
+ )
223
+
224
+
225
+ def promote(self, id: str):
226
+ requestor = api_requestor.APIRequestor(
227
+ client=self._client,
228
+ )
229
+
230
+ response, _, _ = requestor.request(
231
+ options=SeekrFlowRequest(
232
+ method="GET",
233
+ url=f"fine-tunes/{id}/promote-model",
234
+ params={"fine_tune_id": id},
235
+ ),
236
+ stream=False,
237
+ )
238
+
239
+
240
+ assert isinstance(response, SeekrFlowResponse)
241
+
242
+ return FinetuneListEvents(**response.data)
243
+
244
+
245
+ class AsyncFineTuning:
246
+ def __init__(self, client: SeekrFlowClient) -> None:
247
+ self._client = client
248
+
249
+ async def create(
250
+ self,
251
+ *,
252
+ training_file: str,
253
+ model: str,
254
+ n_epochs: int = 1,
255
+ n_checkpoints: int | None = 1,
256
+ batch_size: int | None = 32,
257
+ learning_rate: float = 0.00001,
258
+ suffix: str | None = None,
259
+ wandb_api_key: str | None = None,
260
+ ) -> FinetuneResponse:
261
+ """
262
+ Async method to initiate a fine-tuning job
263
+
264
+ Args:
265
+ training_file (str): File-ID of a file uploaded to the SeekrFlow API
266
+ model (str): Name of the base model to run fine-tune job on
267
+ n_epochs (int, optional): Number of epochs for fine-tuning. Defaults to 1.
268
+ n_checkpoints (int, optional): Number of checkpoints to save during fine-tuning.
269
+ Defaults to 1.
270
+ batch_size (int, optional): Batch size for fine-tuning. Defaults to 32.
271
+ learning_rate (float, optional): Learning rate multiplier to use for training
272
+ Defaults to 0.00001.
273
+ suffix (str, optional): Up to 40 character suffix that will be added to your fine-tuned model name.
274
+ Defaults to None.
275
+ wandb_api_key (str, optional): API key for Weights & Biases integration.
276
+ Defaults to None.
277
+
278
+ Returns:
279
+ FinetuneResponse: Object containing information about fine-tuning job.
280
+ """
281
+
282
+ requestor = api_requestor.APIRequestor(
283
+ client=self._client,
284
+ )
285
+
286
+ parameter_payload = FinetuneRequest(
287
+ model=model,
288
+ training_file=training_file,
289
+ n_epochs=n_epochs,
290
+ n_checkpoints=n_checkpoints,
291
+ batch_size=batch_size,
292
+ learning_rate=learning_rate,
293
+ suffix=suffix,
294
+ wandb_key=wandb_api_key,
295
+ ).model_dump()
296
+
297
+ response, _, _ = await requestor.arequest(
298
+ options=SeekrFlowRequest(
299
+ method="POST",
300
+ url="fine-tunes",
301
+ params=parameter_payload,
302
+ ),
303
+ stream=False,
304
+ )
305
+
306
+ assert isinstance(response, SeekrFlowResponse)
307
+
308
+ return FinetuneResponse(**response.data)
309
+
310
+ async def list(self) -> FinetuneList:
311
+ """
312
+ Async method to list fine-tune job history
313
+
314
+ Returns:
315
+ FinetuneList: Object containing a list of fine-tune jobs
316
+ """
317
+
318
+ requestor = api_requestor.APIRequestor(
319
+ client=self._client,
320
+ )
321
+
322
+ response, _, _ = await requestor.arequest(
323
+ options=SeekrFlowRequest(
324
+ method="GET",
325
+ url="fine-tunes",
326
+ ),
327
+ stream=False,
328
+ )
329
+
330
+ assert isinstance(response, SeekrFlowResponse)
331
+
332
+ return FinetuneList(**response.data)
333
+
334
+ async def retrieve(self, id: str) -> FinetuneResponse:
335
+ """
336
+ Async method to retrieve fine-tune job details
337
+
338
+ Args:
339
+ id (str): Fine-tune ID to retrieve. A string that starts with `ft-`.
340
+
341
+ Returns:
342
+ FinetuneResponse: Object containing information about fine-tuning job.
343
+ """
344
+
345
+ requestor = api_requestor.APIRequestor(
346
+ client=self._client,
347
+ )
348
+
349
+ response, _, _ = await requestor.arequest(
350
+ options=SeekrFlowRequest(
351
+ method="GET",
352
+ url=f"fine-tunes/{id}",
353
+ ),
354
+ stream=False,
355
+ )
356
+
357
+ assert isinstance(response, SeekrFlowResponse)
358
+
359
+ return FinetuneResponse(**response.data)
360
+
361
+ async def cancel(self, id: str) -> FinetuneResponse:
362
+ """
363
+ Async method to cancel a running fine-tuning job
364
+
365
+ Args:
366
+ id (str): Fine-tune ID to cancel. A string that starts with `ft-`.
367
+
368
+ Returns:
369
+ FinetuneResponse: Object containing information about cancelled fine-tuning job.
370
+ """
371
+
372
+ requestor = api_requestor.APIRequestor(
373
+ client=self._client,
374
+ )
375
+
376
+ response, _, _ = await requestor.arequest(
377
+ options=SeekrFlowRequest(
378
+ method="POST",
379
+ url=f"fine-tunes/{id}/cancel",
380
+ ),
381
+ stream=False,
382
+ )
383
+
384
+ assert isinstance(response, SeekrFlowResponse)
385
+
386
+ return FinetuneResponse(**response.data)
387
+
388
+ async def list_events(self, id: str) -> FinetuneListEvents:
389
+ """
390
+ Async method to lists events of a fine-tune job
391
+
392
+ Args:
393
+ id (str): Fine-tune ID to list events for. A string that starts with `ft-`.
394
+
395
+ Returns:
396
+ FinetuneListEvents: Object containing list of fine-tune events
397
+ """
398
+
399
+ requestor = api_requestor.APIRequestor(
400
+ client=self._client,
401
+ )
402
+
403
+ response, _, _ = await requestor.arequest(
404
+ options=SeekrFlowRequest(
405
+ method="GET",
406
+ url=f"fine-tunes/{id}/events",
407
+ ),
408
+ stream=False,
409
+ )
410
+
411
+ assert isinstance(response, SeekrFlowResponse)
412
+
413
+ return FinetuneListEvents(**response.data)
414
+
415
+ async def download(
416
+ self, id: str, *, output: str | None = None, checkpoint_step: int = -1
417
+ ) -> str:
418
+ """
419
+ TODO: Implement async download method
420
+ """
421
+
422
+ raise NotImplementedError(
423
+ "AsyncFineTuning.download not implemented. "
424
+ "Please use FineTuning.download function instead."
425
+ )
@@ -0,0 +1,156 @@
1
+ from __future__ import annotations
2
+
3
+ from seekrai.abstract import api_requestor
4
+ from seekrai.seekrflow_response import SeekrFlowResponse
5
+ from seekrai.types import (
6
+ ImageRequest,
7
+ ImageResponse,
8
+ SeekrFlowClient,
9
+ SeekrFlowRequest,
10
+ )
11
+
12
+
13
+ class Images:
14
+ def __init__(self, client: SeekrFlowClient) -> None:
15
+ self._client = client
16
+
17
+ def generate(
18
+ self,
19
+ *,
20
+ prompt: str,
21
+ model: str,
22
+ steps: int | None = 20,
23
+ seed: int | None = None,
24
+ n: int | None = 1,
25
+ height: int | None = 1024,
26
+ width: int | None = 1024,
27
+ negative_prompt: str | None = None,
28
+ ) -> ImageResponse:
29
+ """
30
+ Method to generate images based on a given prompt using a specified model.
31
+
32
+ Args:
33
+ prompt (str): A description of the desired images. Maximum length varies by model.
34
+
35
+ model (str, optional): The model to use for image generation.
36
+
37
+ steps (int, optional): Number of generation steps. Defaults to 20
38
+
39
+ seed (int, optional): Seed used for generation. Can be used to reproduce image generations.
40
+ Defaults to None.
41
+
42
+ n (int, optional): Number of image results to generate. Defaults to 1.
43
+
44
+ height (int, optional): Height of the image to generate in number of pixels. Defaults to 1024
45
+
46
+ width (int, optional): Width of the image to generate in number of pixels. Defaults to 1024
47
+
48
+ negative_prompt (str, optional): The prompt or prompts not to guide the image generation.
49
+ Defaults to None
50
+
51
+ image_base64: (str, optional): Reference image used for generation. Defaults to None.
52
+
53
+ Returns:
54
+ ImageResponse: Object containing image data
55
+ """
56
+
57
+ requestor = api_requestor.APIRequestor(
58
+ client=self._client,
59
+ )
60
+
61
+ parameter_payload = ImageRequest(
62
+ prompt=prompt,
63
+ model=model,
64
+ steps=steps,
65
+ seed=seed,
66
+ n=n,
67
+ height=height,
68
+ width=width,
69
+ negative_prompt=negative_prompt,
70
+ ).model_dump()
71
+
72
+ response, _, _ = requestor.request(
73
+ options=SeekrFlowRequest(
74
+ method="POST",
75
+ url="images/generations",
76
+ params=parameter_payload,
77
+ ),
78
+ stream=False,
79
+ )
80
+
81
+ assert isinstance(response, SeekrFlowResponse)
82
+
83
+ return ImageResponse(**response.data)
84
+
85
+
86
+ class AsyncImages:
87
+ def __init__(self, client: SeekrFlowClient) -> None:
88
+ self._client = client
89
+
90
+ async def generate(
91
+ self,
92
+ *,
93
+ prompt: str,
94
+ model: str,
95
+ steps: int | None = 20,
96
+ seed: int | None = None,
97
+ n: int | None = 1,
98
+ height: int | None = 1024,
99
+ width: int | None = 1024,
100
+ negative_prompt: str | None = None,
101
+ ) -> ImageResponse:
102
+ """
103
+ Async method to generate images based on a given prompt using a specified model.
104
+
105
+ Args:
106
+ prompt (str): A description of the desired images. Maximum length varies by model.
107
+
108
+ model (str, optional): The model to use for image generation.
109
+
110
+ steps (int, optional): Number of generation steps. Defaults to 20
111
+
112
+ seed (int, optional): Seed used for generation. Can be used to reproduce image generations.
113
+ Defaults to None.
114
+
115
+ n (int, optional): Number of image results to generate. Defaults to 1.
116
+
117
+ height (int, optional): Height of the image to generate in number of pixels. Defaults to 1024
118
+
119
+ width (int, optional): Width of the image to generate in number of pixels. Defaults to 1024
120
+
121
+ negative_prompt (str, optional): The prompt or prompts not to guide the image generation.
122
+ Defaults to None
123
+
124
+ image_base64: (str, optional): Reference image used for generation. Defaults to None.
125
+
126
+ Returns:
127
+ ImageResponse: Object containing image data
128
+ """
129
+
130
+ requestor = api_requestor.APIRequestor(
131
+ client=self._client,
132
+ )
133
+
134
+ parameter_payload = ImageRequest(
135
+ prompt=prompt,
136
+ model=model,
137
+ steps=steps,
138
+ seed=seed,
139
+ n=n,
140
+ height=height,
141
+ width=width,
142
+ negative_prompt=negative_prompt,
143
+ ).model_dump()
144
+
145
+ response, _, _ = await requestor.arequest(
146
+ options=SeekrFlowRequest(
147
+ method="POST",
148
+ url="images/generations",
149
+ params=parameter_payload,
150
+ ),
151
+ stream=False,
152
+ )
153
+
154
+ assert isinstance(response, SeekrFlowResponse)
155
+
156
+ return ImageResponse(**response.data)
@@ -0,0 +1,75 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import List
4
+
5
+ from seekrai.abstract import api_requestor
6
+ from seekrai.seekrflow_response import SeekrFlowResponse
7
+ from seekrai.types import (
8
+ ModelObject,
9
+ SeekrFlowClient,
10
+ SeekrFlowRequest,
11
+ )
12
+
13
+
14
+ class Models:
15
+ def __init__(self, client: SeekrFlowClient) -> None:
16
+ self._client = client
17
+
18
+ def list(
19
+ self,
20
+ ) -> List[ModelObject]:
21
+ """
22
+ Method to return list of models on the API
23
+
24
+ Returns:
25
+ List[ModelObject]: List of model objects
26
+ """
27
+
28
+ requestor = api_requestor.APIRequestor(
29
+ client=self._client,
30
+ )
31
+
32
+ response, _, _ = requestor.request(
33
+ options=SeekrFlowRequest(
34
+ method="GET",
35
+ url="models",
36
+ ),
37
+ stream=False,
38
+ )
39
+
40
+ assert isinstance(response, SeekrFlowResponse)
41
+ assert isinstance(response.data, list)
42
+
43
+ return [ModelObject(**model) for model in response.data]
44
+
45
+
46
+ class AsyncModels:
47
+ def __init__(self, client: SeekrFlowClient) -> None:
48
+ self._client = client
49
+
50
+ async def list(
51
+ self,
52
+ ) -> List[ModelObject]:
53
+ """
54
+ Async method to return list of models on API
55
+
56
+ Returns:
57
+ List[ModelObject]: List of model objects
58
+ """
59
+
60
+ requestor = api_requestor.APIRequestor(
61
+ client=self._client,
62
+ )
63
+
64
+ response, _, _ = await requestor.arequest(
65
+ options=SeekrFlowRequest(
66
+ method="GET",
67
+ url="models",
68
+ ),
69
+ stream=False,
70
+ )
71
+
72
+ assert isinstance(response, SeekrFlowResponse)
73
+ assert isinstance(response.data, list)
74
+
75
+ return [ModelObject(**model) for model in response.data]
@@ -0,0 +1,50 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Dict
4
+
5
+
6
+ class SeekrFlowResponse:
7
+ """
8
+ API Response class. Stores headers and response data.
9
+ """
10
+
11
+ def __init__(self, data: Dict[str, Any], headers: Dict[str, Any]):
12
+ self._headers = headers
13
+ self.data = data
14
+
15
+ @property
16
+ def request_id(self) -> str | None:
17
+ """
18
+ Fetches request id from headers
19
+ """
20
+ if "cf-ray" in self._headers:
21
+ return str(self._headers["cf-ray"])
22
+ return None
23
+
24
+ @property
25
+ def requests_remaining(self) -> int | None:
26
+ """
27
+ Number of requests remaining at current rate limit
28
+ """
29
+ if "x-ratelimit-remaining" in self._headers:
30
+ return int(self._headers["x-ratelimit-remaining"])
31
+ return None
32
+
33
+ @property
34
+ def processed_by(self) -> str | None:
35
+ """
36
+ Processing host server name
37
+ """
38
+ if "x-hostname" in self._headers:
39
+ return str(self._headers["x-hostname"])
40
+ return None
41
+
42
+ @property
43
+ def response_ms(self) -> int | None:
44
+ """
45
+ Server request completion time
46
+ """
47
+ if "x-total-time" in self._headers:
48
+ h = self._headers["x-total-time"]
49
+ return None if h is None else round(float(h))
50
+ return None