pltr-cli 0.11.0__py3-none-any.whl → 0.13.0__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 (46) hide show
  1. pltr/__init__.py +1 -1
  2. pltr/cli.py +40 -0
  3. pltr/commands/admin.py +565 -11
  4. pltr/commands/aip_agents.py +333 -0
  5. pltr/commands/connectivity.py +309 -1
  6. pltr/commands/cp.py +103 -0
  7. pltr/commands/dataset.py +104 -4
  8. pltr/commands/functions.py +503 -0
  9. pltr/commands/language_models.py +515 -0
  10. pltr/commands/mediasets.py +176 -0
  11. pltr/commands/models.py +362 -0
  12. pltr/commands/ontology.py +44 -13
  13. pltr/commands/orchestration.py +167 -11
  14. pltr/commands/project.py +231 -22
  15. pltr/commands/resource.py +416 -17
  16. pltr/commands/space.py +25 -303
  17. pltr/commands/sql.py +54 -7
  18. pltr/commands/streams.py +616 -0
  19. pltr/commands/third_party_applications.py +82 -0
  20. pltr/services/admin.py +331 -3
  21. pltr/services/aip_agents.py +147 -0
  22. pltr/services/base.py +104 -1
  23. pltr/services/connectivity.py +139 -0
  24. pltr/services/copy.py +391 -0
  25. pltr/services/dataset.py +77 -4
  26. pltr/services/folder.py +6 -1
  27. pltr/services/functions.py +223 -0
  28. pltr/services/language_models.py +281 -0
  29. pltr/services/mediasets.py +144 -9
  30. pltr/services/models.py +179 -0
  31. pltr/services/ontology.py +48 -1
  32. pltr/services/orchestration.py +133 -1
  33. pltr/services/project.py +213 -39
  34. pltr/services/resource.py +229 -60
  35. pltr/services/space.py +24 -175
  36. pltr/services/sql.py +44 -20
  37. pltr/services/streams.py +290 -0
  38. pltr/services/third_party_applications.py +53 -0
  39. pltr/utils/formatting.py +195 -1
  40. pltr/utils/pagination.py +325 -0
  41. {pltr_cli-0.11.0.dist-info → pltr_cli-0.13.0.dist-info}/METADATA +55 -4
  42. pltr_cli-0.13.0.dist-info/RECORD +70 -0
  43. {pltr_cli-0.11.0.dist-info → pltr_cli-0.13.0.dist-info}/WHEEL +1 -1
  44. pltr_cli-0.11.0.dist-info/RECORD +0 -55
  45. {pltr_cli-0.11.0.dist-info → pltr_cli-0.13.0.dist-info}/entry_points.txt +0 -0
  46. {pltr_cli-0.11.0.dist-info → pltr_cli-0.13.0.dist-info}/licenses/LICENSE +0 -0
pltr/services/admin.py CHANGED
@@ -3,10 +3,12 @@ Admin service wrapper for Foundry SDK admin operations.
3
3
  Provides a high-level interface for user, group, role, and organization management.
4
4
  """
5
5
 
6
- from typing import Any, Dict, Optional
6
+ from typing import Any, Dict, List, Optional, Callable
7
7
  import json
8
8
 
9
9
  from .base import BaseService
10
+ from ..utils.pagination import PaginationConfig, PaginationResult
11
+ from ..config.settings import Settings
10
12
 
11
13
 
12
14
  class AdminService(BaseService):
@@ -23,6 +25,8 @@ class AdminService(BaseService):
23
25
  """
24
26
  List all users in the organization.
25
27
 
28
+ DEPRECATED: Use list_users_paginated() instead for better pagination support.
29
+
26
30
  Args:
27
31
  page_size: Maximum number of users to return per page
28
32
  page_token: Token for pagination (from previous response)
@@ -38,6 +42,47 @@ class AdminService(BaseService):
38
42
  except Exception as e:
39
43
  raise RuntimeError(f"Failed to list users: {str(e)}")
40
44
 
45
+ def list_users_paginated(
46
+ self,
47
+ config: PaginationConfig,
48
+ progress_callback: Optional[Callable[[int, int], None]] = None,
49
+ ) -> PaginationResult:
50
+ """
51
+ List users with full pagination control.
52
+
53
+ Args:
54
+ config: Pagination configuration (page_size, max_pages, etc.)
55
+ progress_callback: Optional callback(page_num, items_count)
56
+
57
+ Returns:
58
+ PaginationResult with users and metadata
59
+
60
+ Example:
61
+ >>> config = PaginationConfig(page_size=50, max_pages=2)
62
+ >>> result = service.list_users_paginated(config)
63
+ >>> print(f"Fetched {result.metadata.items_fetched} users")
64
+ """
65
+ try:
66
+ settings = Settings()
67
+
68
+ def fetch_page(page_token: Optional[str]) -> Dict[str, Any]:
69
+ """Fetch a single page of users."""
70
+ iterator = self.service.User.list(
71
+ page_size=config.page_size or settings.get("page_size", 20),
72
+ page_token=page_token,
73
+ )
74
+ # ResourceIterator has .data and .next_page_token attributes
75
+ # Extract them properly for the pagination handler
76
+ return {
77
+ "data": [self._serialize_response(user) for user in iterator.data],
78
+ "next_page_token": iterator.next_page_token,
79
+ }
80
+
81
+ # Use response pagination handler
82
+ return self._paginate_response(fetch_page, config, progress_callback)
83
+ except Exception as e:
84
+ raise RuntimeError(f"Failed to list users: {str(e)}")
85
+
41
86
  def get_user(self, user_id: str) -> Dict[str, Any]:
42
87
  """
43
88
  Get a specific user by ID.
@@ -127,6 +172,43 @@ class AdminService(BaseService):
127
172
  except Exception as e:
128
173
  raise RuntimeError(f"Failed to revoke tokens for user {user_id}: {str(e)}")
129
174
 
175
+ def delete_user(self, user_id: str) -> Dict[str, Any]:
176
+ """
177
+ Delete a specific user.
178
+
179
+ Args:
180
+ user_id: The user ID or RID
181
+
182
+ Returns:
183
+ Dictionary containing operation result
184
+ """
185
+ try:
186
+ self.service.User.delete(user_id)
187
+ return {
188
+ "success": True,
189
+ "message": f"User {user_id} deleted successfully",
190
+ }
191
+ except Exception as e:
192
+ raise RuntimeError(f"Failed to delete user {user_id}: {str(e)}")
193
+
194
+ def get_batch_users(self, user_ids: List[str]) -> Dict[str, Any]:
195
+ """
196
+ Batch retrieve multiple users (up to 500).
197
+
198
+ Args:
199
+ user_ids: List of user IDs or RIDs
200
+
201
+ Returns:
202
+ Dictionary containing user information
203
+ """
204
+ if len(user_ids) > 500:
205
+ raise ValueError("Maximum batch size is 500 users")
206
+ try:
207
+ response = self.service.User.get_batch(body=user_ids)
208
+ return self._serialize_response(response)
209
+ except Exception as e:
210
+ raise RuntimeError(f"Failed to get users batch: {str(e)}")
211
+
130
212
  # Group Management Methods
131
213
  def list_groups(
132
214
  self, page_size: Optional[int] = None, page_token: Optional[str] = None
@@ -142,10 +224,16 @@ class AdminService(BaseService):
142
224
  Dictionary containing group list and pagination info
143
225
  """
144
226
  try:
145
- response = self.service.Group.list(
227
+ iterator = self.service.Group.list(
146
228
  page_size=page_size, page_token=page_token
147
229
  )
148
- return self._serialize_response(response)
230
+ # ResourceIterator has .data and .next_page_token attributes
231
+ # Return structure compatible with formatter.display()
232
+ groups = [self._serialize_response(group) for group in iterator.data]
233
+ return {
234
+ "data": groups,
235
+ "next_page_token": iterator.next_page_token,
236
+ }
149
237
  except Exception as e:
150
238
  raise RuntimeError(f"Failed to list groups: {str(e)}")
151
239
 
@@ -239,6 +327,136 @@ class AdminService(BaseService):
239
327
  except Exception as e:
240
328
  raise RuntimeError(f"Failed to delete group {group_id}: {str(e)}")
241
329
 
330
+ def get_batch_groups(self, group_ids: List[str]) -> Dict[str, Any]:
331
+ """
332
+ Batch retrieve multiple groups (up to 500).
333
+
334
+ Args:
335
+ group_ids: List of group IDs or RIDs
336
+
337
+ Returns:
338
+ Dictionary containing group information
339
+ """
340
+ if len(group_ids) > 500:
341
+ raise ValueError("Maximum batch size is 500 groups")
342
+ try:
343
+ response = self.service.Group.get_batch(body=group_ids)
344
+ return self._serialize_response(response)
345
+ except Exception as e:
346
+ raise RuntimeError(f"Failed to get groups batch: {str(e)}")
347
+
348
+ # Marking Management Methods
349
+ def list_markings(
350
+ self, page_size: Optional[int] = None, page_token: Optional[str] = None
351
+ ) -> Dict[str, Any]:
352
+ """
353
+ List all markings.
354
+
355
+ Args:
356
+ page_size: Maximum number of markings to return per page
357
+ page_token: Token for pagination (from previous response)
358
+
359
+ Returns:
360
+ Dictionary containing marking list and pagination info
361
+ """
362
+ try:
363
+ response = self.service.Marking.list(
364
+ page_size=page_size, page_token=page_token, preview=True
365
+ )
366
+ return self._serialize_response(response)
367
+ except Exception as e:
368
+ raise RuntimeError(f"Failed to list markings: {str(e)}")
369
+
370
+ def get_marking(self, marking_id: str) -> Dict[str, Any]:
371
+ """
372
+ Get a specific marking by ID.
373
+
374
+ Args:
375
+ marking_id: The marking ID
376
+
377
+ Returns:
378
+ Dictionary containing marking information
379
+ """
380
+ try:
381
+ response = self.service.Marking.get(marking_id, preview=True)
382
+ return self._serialize_response(response)
383
+ except Exception as e:
384
+ raise RuntimeError(f"Failed to get marking {marking_id}: {str(e)}")
385
+
386
+ def get_batch_markings(self, marking_ids: List[str]) -> Dict[str, Any]:
387
+ """
388
+ Batch retrieve multiple markings (up to 500).
389
+
390
+ Args:
391
+ marking_ids: List of marking IDs
392
+
393
+ Returns:
394
+ Dictionary containing marking information
395
+ """
396
+ if len(marking_ids) > 500:
397
+ raise ValueError("Maximum batch size is 500 markings")
398
+ try:
399
+ response = self.service.Marking.get_batch(body=marking_ids, preview=True)
400
+ return self._serialize_response(response)
401
+ except Exception as e:
402
+ raise RuntimeError(f"Failed to get markings batch: {str(e)}")
403
+
404
+ def create_marking(
405
+ self,
406
+ name: str,
407
+ description: Optional[str] = None,
408
+ category_id: Optional[str] = None,
409
+ ) -> Dict[str, Any]:
410
+ """
411
+ Create a new marking.
412
+
413
+ Args:
414
+ name: The marking name
415
+ description: Optional marking description
416
+ category_id: Optional category ID for the marking
417
+
418
+ Returns:
419
+ Dictionary containing created marking information
420
+ """
421
+ try:
422
+ create_params: Dict[str, Any] = {"name": name, "preview": True}
423
+ if description:
424
+ create_params["description"] = description
425
+ if category_id:
426
+ create_params["category_id"] = category_id
427
+
428
+ response = self.service.Marking.create(**create_params)
429
+ return self._serialize_response(response)
430
+ except Exception as e:
431
+ raise RuntimeError(f"Failed to create marking '{name}': {str(e)}")
432
+
433
+ def replace_marking(
434
+ self,
435
+ marking_id: str,
436
+ name: str,
437
+ description: Optional[str] = None,
438
+ ) -> Dict[str, Any]:
439
+ """
440
+ Replace/update an existing marking.
441
+
442
+ Args:
443
+ marking_id: The marking ID
444
+ name: The new marking name
445
+ description: Optional new marking description
446
+
447
+ Returns:
448
+ Dictionary containing updated marking information
449
+ """
450
+ try:
451
+ replace_params: Dict[str, Any] = {"name": name, "preview": True}
452
+ if description:
453
+ replace_params["description"] = description
454
+
455
+ response = self.service.Marking.replace(marking_id, **replace_params)
456
+ return self._serialize_response(response)
457
+ except Exception as e:
458
+ raise RuntimeError(f"Failed to replace marking {marking_id}: {str(e)}")
459
+
242
460
  # Organization Management Methods
243
461
  def get_organization(self, organization_id: str) -> Dict[str, Any]:
244
462
  """
@@ -258,6 +476,98 @@ class AdminService(BaseService):
258
476
  f"Failed to get organization {organization_id}: {str(e)}"
259
477
  )
260
478
 
479
+ def create_organization(
480
+ self,
481
+ name: str,
482
+ enrollment_rid: str,
483
+ admin_ids: Optional[List[str]] = None,
484
+ ) -> Dict[str, Any]:
485
+ """
486
+ Create a new organization.
487
+
488
+ Args:
489
+ name: The organization name
490
+ enrollment_rid: The enrollment RID
491
+ admin_ids: Optional list of admin user IDs
492
+
493
+ Returns:
494
+ Dictionary containing created organization information
495
+ """
496
+ try:
497
+ create_params: Dict[str, Any] = {
498
+ "name": name,
499
+ "enrollment_rid": enrollment_rid,
500
+ "preview": True,
501
+ }
502
+ if admin_ids:
503
+ create_params["admin_ids"] = admin_ids
504
+
505
+ response = self.service.Organization.create(**create_params)
506
+ return self._serialize_response(response)
507
+ except Exception as e:
508
+ raise RuntimeError(f"Failed to create organization '{name}': {str(e)}")
509
+
510
+ def replace_organization(
511
+ self,
512
+ organization_rid: str,
513
+ name: str,
514
+ description: Optional[str] = None,
515
+ ) -> Dict[str, Any]:
516
+ """
517
+ Replace/update an existing organization.
518
+
519
+ Args:
520
+ organization_rid: The organization RID
521
+ name: The new organization name
522
+ description: Optional new organization description
523
+
524
+ Returns:
525
+ Dictionary containing updated organization information
526
+ """
527
+ try:
528
+ replace_params: Dict[str, Any] = {"name": name, "preview": True}
529
+ if description:
530
+ replace_params["description"] = description
531
+
532
+ response = self.service.Organization.replace(
533
+ organization_rid, **replace_params
534
+ )
535
+ return self._serialize_response(response)
536
+ except Exception as e:
537
+ raise RuntimeError(
538
+ f"Failed to replace organization {organization_rid}: {str(e)}"
539
+ )
540
+
541
+ def list_available_roles(
542
+ self,
543
+ organization_rid: str,
544
+ page_size: Optional[int] = None,
545
+ page_token: Optional[str] = None,
546
+ ) -> Dict[str, Any]:
547
+ """
548
+ List available roles for an organization.
549
+
550
+ Args:
551
+ organization_rid: The organization RID
552
+ page_size: Maximum number of roles to return per page
553
+ page_token: Token for pagination (from previous response)
554
+
555
+ Returns:
556
+ Dictionary containing role list and pagination info
557
+ """
558
+ try:
559
+ response = self.service.Organization.list_available_roles(
560
+ organization_rid,
561
+ page_size=page_size,
562
+ page_token=page_token,
563
+ preview=True,
564
+ )
565
+ return self._serialize_response(response)
566
+ except Exception as e:
567
+ raise RuntimeError(
568
+ f"Failed to list available roles for organization {organization_rid}: {str(e)}"
569
+ )
570
+
261
571
  # Role Management Methods
262
572
  def get_role(self, role_id: str) -> Dict[str, Any]:
263
573
  """
@@ -275,6 +585,24 @@ class AdminService(BaseService):
275
585
  except Exception as e:
276
586
  raise RuntimeError(f"Failed to get role {role_id}: {str(e)}")
277
587
 
588
+ def get_batch_roles(self, role_ids: List[str]) -> Dict[str, Any]:
589
+ """
590
+ Batch retrieve multiple roles (up to 500).
591
+
592
+ Args:
593
+ role_ids: List of role IDs or RIDs
594
+
595
+ Returns:
596
+ Dictionary containing role information
597
+ """
598
+ if len(role_ids) > 500:
599
+ raise ValueError("Maximum batch size is 500 roles")
600
+ try:
601
+ response = self.service.Role.get_batch(body=role_ids, preview=True)
602
+ return self._serialize_response(response)
603
+ except Exception as e:
604
+ raise RuntimeError(f"Failed to get roles batch: {str(e)}")
605
+
278
606
  def _serialize_response(self, response: Any) -> Dict[str, Any]:
279
607
  """
280
608
  Convert response object to serializable dictionary.
@@ -0,0 +1,147 @@
1
+ """
2
+ AIP Agents service wrapper for Foundry SDK.
3
+ Provides access to AIP Agent operations including agent details, sessions, and versions.
4
+ """
5
+
6
+ from typing import Any, Dict, Optional
7
+ from .base import BaseService
8
+ from ..utils.pagination import PaginationConfig, PaginationResult
9
+
10
+
11
+ class AipAgentsService(BaseService):
12
+ """Service wrapper for Foundry AIP Agents operations."""
13
+
14
+ def _get_service(self) -> Any:
15
+ """Get the Foundry AIP agents service."""
16
+ return self.client.aip_agents
17
+
18
+ # ===== Agent Operations =====
19
+
20
+ def get_agent(
21
+ self, agent_rid: str, version: Optional[str] = None, preview: bool = True
22
+ ) -> Dict[str, Any]:
23
+ """
24
+ Get details for an AIP Agent.
25
+
26
+ Args:
27
+ agent_rid: Agent Resource Identifier
28
+ Format: ri.foundry.main.agent.<id>
29
+ version: Optional agent version string (e.g., "1.0")
30
+ If not specified, returns latest published version
31
+ preview: Enable preview mode (default: True)
32
+
33
+ Returns:
34
+ Agent information dictionary containing:
35
+ - rid: Agent resource identifier
36
+ - version: Agent version
37
+ - metadata: Agent metadata (displayName, description, etc.)
38
+ - parameters: Application variables/parameters
39
+
40
+ Raises:
41
+ RuntimeError: If the operation fails
42
+
43
+ Example:
44
+ >>> service = AipAgentsService()
45
+ >>> agent = service.get_agent("ri.foundry.main.agent.abc123")
46
+ >>> print(agent['metadata']['displayName'])
47
+ """
48
+ try:
49
+ agent = self.service.Agent.get(agent_rid, version=version, preview=preview)
50
+ return self._serialize_response(agent)
51
+ except Exception as e:
52
+ raise RuntimeError(f"Failed to get agent {agent_rid}: {e}")
53
+
54
+ # ===== Session Operations =====
55
+
56
+ def list_sessions(
57
+ self, agent_rid: str, config: PaginationConfig, preview: bool = True
58
+ ) -> PaginationResult:
59
+ """
60
+ List all conversation sessions for an agent created by the calling user.
61
+
62
+ Note: Only lists sessions created by this client, not sessions created
63
+ in AIP Agent Studio.
64
+
65
+ Args:
66
+ agent_rid: Agent Resource Identifier
67
+ config: Pagination configuration
68
+ preview: Enable preview mode (default: True)
69
+
70
+ Returns:
71
+ PaginationResult with sessions and metadata
72
+
73
+ Example:
74
+ >>> config = PaginationConfig(page_size=20, max_pages=2)
75
+ >>> result = service.list_sessions(agent_rid, config)
76
+ >>> print(f"Found {result.metadata.items_fetched} sessions")
77
+ """
78
+ try:
79
+ iterator = self.service.Agent.Session.list(
80
+ agent_rid, page_size=config.page_size, preview=preview
81
+ )
82
+ return self._paginate_iterator(iterator, config)
83
+ except Exception as e:
84
+ raise RuntimeError(f"Failed to list sessions for agent {agent_rid}: {e}")
85
+
86
+ def get_session(
87
+ self, agent_rid: str, session_rid: str, preview: bool = True
88
+ ) -> Dict[str, Any]:
89
+ """
90
+ Get details of a specific conversation session.
91
+
92
+ Args:
93
+ agent_rid: Agent Resource Identifier
94
+ session_rid: Session Resource Identifier
95
+ preview: Enable preview mode (default: True)
96
+
97
+ Returns:
98
+ Session information dictionary containing:
99
+ - rid: Session resource identifier
100
+ - agent_rid: Associated agent RID
101
+ - agent_version: Agent version used in session
102
+ - metadata: Session metadata (e.g., title)
103
+
104
+ Raises:
105
+ RuntimeError: If the operation fails
106
+ """
107
+ try:
108
+ session = self.service.Agent.Session.get(
109
+ agent_rid, session_rid, preview=preview
110
+ )
111
+ return self._serialize_response(session)
112
+ except Exception as e:
113
+ raise RuntimeError(
114
+ f"Failed to get session {session_rid} for agent {agent_rid}: {e}"
115
+ )
116
+
117
+ # ===== Version Operations =====
118
+
119
+ def list_versions(
120
+ self, agent_rid: str, config: PaginationConfig, preview: bool = True
121
+ ) -> PaginationResult:
122
+ """
123
+ List all versions for an AIP Agent.
124
+
125
+ Versions are returned in descending order (most recent first).
126
+
127
+ Args:
128
+ agent_rid: Agent Resource Identifier
129
+ config: Pagination configuration
130
+ preview: Enable preview mode (default: True)
131
+
132
+ Returns:
133
+ PaginationResult with agent versions and metadata
134
+
135
+ Example:
136
+ >>> config = PaginationConfig(page_size=10, fetch_all=True)
137
+ >>> result = service.list_versions(agent_rid, config)
138
+ >>> for version in result.data:
139
+ ... print(f"Version {version['string']}")
140
+ """
141
+ try:
142
+ iterator = self.service.Agent.AgentVersion.list(
143
+ agent_rid, page_size=config.page_size, preview=preview
144
+ )
145
+ return self._paginate_iterator(iterator, config)
146
+ except Exception as e:
147
+ raise RuntimeError(f"Failed to list versions for agent {agent_rid}: {e}")
pltr/services/base.py CHANGED
@@ -2,13 +2,20 @@
2
2
  Base service class for Foundry API wrappers.
3
3
  """
4
4
 
5
- from typing import Any, Optional, Dict
5
+ from typing import Any, Optional, Dict, Callable, Iterator
6
6
  from abc import ABC, abstractmethod
7
+ import json
7
8
  import requests
8
9
 
9
10
  from ..auth.manager import AuthManager
10
11
  from ..auth.storage import CredentialStorage
11
12
  from ..config.profiles import ProfileManager
13
+ from ..utils.pagination import (
14
+ PaginationConfig,
15
+ PaginationResult,
16
+ IteratorPaginationHandler,
17
+ ResponsePaginationHandler,
18
+ )
12
19
 
13
20
 
14
21
  class BaseService(ABC):
@@ -127,3 +134,99 @@ class BaseService(ABC):
127
134
  response.raise_for_status()
128
135
 
129
136
  return response
137
+
138
+ def _paginate_iterator(
139
+ self,
140
+ iterator: Iterator[Any],
141
+ config: PaginationConfig,
142
+ progress_callback: Optional[Callable[[int, int], None]] = None,
143
+ ) -> PaginationResult:
144
+ """
145
+ Handle pagination for iterator-based SDK methods.
146
+
147
+ Args:
148
+ iterator: Iterator returned from SDK (e.g., ResourceIterator)
149
+ config: Pagination configuration
150
+ progress_callback: Optional progress callback
151
+
152
+ Returns:
153
+ PaginationResult with collected items and metadata
154
+
155
+ Example:
156
+ >>> iterator = self.service.Dataset.File.list(dataset_rid, page_size=20)
157
+ >>> result = self._paginate_iterator(iterator, config)
158
+ """
159
+ handler = IteratorPaginationHandler()
160
+ return handler.collect_pages(iterator, config, progress_callback)
161
+
162
+ def _paginate_response(
163
+ self,
164
+ fetch_fn: Callable[[Optional[str]], Dict[str, Any]],
165
+ config: PaginationConfig,
166
+ progress_callback: Optional[Callable[[int, int], None]] = None,
167
+ ) -> PaginationResult:
168
+ """
169
+ Handle pagination for response-based SDK methods.
170
+
171
+ Args:
172
+ fetch_fn: Function that accepts page_token and returns dict with
173
+ 'data' and 'next_page_token' keys
174
+ config: Pagination configuration
175
+ progress_callback: Optional progress callback
176
+
177
+ Returns:
178
+ PaginationResult with collected items and metadata
179
+
180
+ Example:
181
+ >>> def fetch(token):
182
+ ... response = self.service.User.list(page_token=token)
183
+ ... return self._serialize_response(response)
184
+ >>> result = self._paginate_response(fetch, config)
185
+ """
186
+ handler = ResponsePaginationHandler()
187
+ return handler.collect_pages(fetch_fn, config, progress_callback)
188
+
189
+ def _serialize_response(self, response: Any) -> Dict[str, Any]:
190
+ """
191
+ Convert response object to serializable dictionary.
192
+
193
+ This handles various SDK response types including Pydantic models,
194
+ regular objects, and primitives.
195
+
196
+ Args:
197
+ response: Response object from SDK
198
+
199
+ Returns:
200
+ Serializable dictionary representation
201
+
202
+ Note:
203
+ This method was moved from AdminService to provide consistent
204
+ serialization across all services.
205
+ """
206
+ if response is None:
207
+ return {}
208
+
209
+ # Handle different response types
210
+ if hasattr(response, "dict"):
211
+ # Pydantic models
212
+ return response.dict()
213
+ elif hasattr(response, "__dict__"):
214
+ # Regular objects
215
+ result = {}
216
+ for key, value in response.__dict__.items():
217
+ if not key.startswith("_"):
218
+ try:
219
+ # Try to serialize the value
220
+ json.dumps(value)
221
+ result[key] = value
222
+ except (TypeError, ValueError):
223
+ # Convert non-serializable values to string
224
+ result[key] = str(value)
225
+ return result
226
+ else:
227
+ # Primitive types or already serializable
228
+ try:
229
+ json.dumps(response)
230
+ return response
231
+ except (TypeError, ValueError):
232
+ return {"data": str(response)}