pygeai 0.6.0b6__py3-none-any.whl → 0.6.0b10__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 (227) hide show
  1. pygeai/_docs/source/conf.py +78 -6
  2. pygeai/_docs/source/content/api_reference/admin.rst +161 -0
  3. pygeai/_docs/source/content/api_reference/assistant.rst +326 -0
  4. pygeai/_docs/source/content/api_reference/auth.rst +379 -0
  5. pygeai/_docs/source/content/api_reference/embeddings.rst +31 -1
  6. pygeai/_docs/source/content/api_reference/evaluation.rst +590 -0
  7. pygeai/_docs/source/content/api_reference/feedback.rst +237 -0
  8. pygeai/_docs/source/content/api_reference/files.rst +592 -0
  9. pygeai/_docs/source/content/api_reference/gam.rst +401 -0
  10. pygeai/_docs/source/content/api_reference/health.rst +58 -0
  11. pygeai/_docs/source/content/api_reference/project.rst +20 -18
  12. pygeai/_docs/source/content/api_reference/proxy.rst +318 -0
  13. pygeai/_docs/source/content/api_reference/rerank.rst +94 -0
  14. pygeai/_docs/source/content/api_reference/secrets.rst +495 -0
  15. pygeai/_docs/source/content/api_reference/usage_limits.rst +390 -0
  16. pygeai/_docs/source/content/api_reference.rst +13 -1
  17. pygeai/_docs/source/content/debugger.rst +376 -83
  18. pygeai/_docs/source/content/migration.rst +528 -0
  19. pygeai/_docs/source/content/modules.rst +1 -1
  20. pygeai/_docs/source/index.rst +59 -7
  21. pygeai/_docs/source/pygeai.auth.rst +29 -0
  22. pygeai/_docs/source/pygeai.cli.commands.rst +16 -0
  23. pygeai/_docs/source/pygeai.cli.rst +8 -0
  24. pygeai/_docs/source/pygeai.core.utils.rst +16 -0
  25. pygeai/_docs/source/pygeai.rst +1 -0
  26. pygeai/_docs/source/pygeai.tests.auth.rst +21 -0
  27. pygeai/_docs/source/pygeai.tests.cli.commands.rst +16 -0
  28. pygeai/_docs/source/pygeai.tests.cli.rst +16 -0
  29. pygeai/_docs/source/pygeai.tests.core.base.rst +8 -0
  30. pygeai/_docs/source/pygeai.tests.core.embeddings.rst +16 -0
  31. pygeai/_docs/source/pygeai.tests.core.files.rst +8 -0
  32. pygeai/_docs/source/pygeai.tests.core.plugins.rst +21 -0
  33. pygeai/_docs/source/pygeai.tests.core.rst +1 -0
  34. pygeai/_docs/source/pygeai.tests.evaluation.dataset.rst +21 -0
  35. pygeai/_docs/source/pygeai.tests.evaluation.plan.rst +21 -0
  36. pygeai/_docs/source/pygeai.tests.evaluation.result.rst +21 -0
  37. pygeai/_docs/source/pygeai.tests.evaluation.rst +20 -0
  38. pygeai/_docs/source/pygeai.tests.integration.lab.processes.rst +8 -0
  39. pygeai/_docs/source/pygeai.tests.organization.rst +8 -0
  40. pygeai/_docs/source/pygeai.tests.rst +2 -0
  41. pygeai/_docs/source/pygeai.tests.snippets.auth.rst +10 -0
  42. pygeai/_docs/source/pygeai.tests.snippets.chat.rst +40 -0
  43. pygeai/_docs/source/pygeai.tests.snippets.dbg.rst +45 -0
  44. pygeai/_docs/source/pygeai.tests.snippets.embeddings.rst +40 -0
  45. pygeai/_docs/source/pygeai.tests.snippets.evaluation.dataset.rst +197 -0
  46. pygeai/_docs/source/pygeai.tests.snippets.evaluation.plan.rst +133 -0
  47. pygeai/_docs/source/pygeai.tests.snippets.evaluation.result.rst +37 -0
  48. pygeai/_docs/source/pygeai.tests.snippets.evaluation.rst +10 -0
  49. pygeai/_docs/source/pygeai.tests.snippets.organization.rst +40 -0
  50. pygeai/_docs/source/pygeai.tests.snippets.rst +2 -0
  51. pygeai/admin/clients.py +12 -32
  52. pygeai/assistant/clients.py +16 -44
  53. pygeai/assistant/data/clients.py +1 -0
  54. pygeai/assistant/data_analyst/clients.py +6 -13
  55. pygeai/assistant/rag/clients.py +24 -67
  56. pygeai/auth/clients.py +88 -14
  57. pygeai/auth/endpoints.py +4 -0
  58. pygeai/chat/clients.py +192 -25
  59. pygeai/chat/endpoints.py +2 -1
  60. pygeai/cli/commands/auth.py +178 -2
  61. pygeai/cli/commands/chat.py +227 -1
  62. pygeai/cli/commands/embeddings.py +56 -8
  63. pygeai/cli/commands/lab/ai_lab.py +0 -2
  64. pygeai/cli/commands/migrate.py +994 -434
  65. pygeai/cli/commands/organization.py +241 -0
  66. pygeai/cli/error_handler.py +116 -0
  67. pygeai/cli/geai.py +28 -10
  68. pygeai/cli/parsers.py +8 -2
  69. pygeai/core/base/clients.py +4 -1
  70. pygeai/core/common/exceptions.py +11 -10
  71. pygeai/core/embeddings/__init__.py +19 -0
  72. pygeai/core/embeddings/clients.py +20 -9
  73. pygeai/core/embeddings/mappers.py +16 -2
  74. pygeai/core/embeddings/responses.py +9 -2
  75. pygeai/core/feedback/clients.py +4 -8
  76. pygeai/core/files/clients.py +10 -25
  77. pygeai/core/files/managers.py +42 -0
  78. pygeai/core/llm/clients.py +11 -26
  79. pygeai/core/models.py +107 -0
  80. pygeai/core/plugins/clients.py +4 -7
  81. pygeai/core/rerank/clients.py +4 -8
  82. pygeai/core/secrets/clients.py +14 -37
  83. pygeai/core/services/rest.py +1 -1
  84. pygeai/core/utils/parsers.py +32 -0
  85. pygeai/core/utils/validators.py +10 -0
  86. pygeai/dbg/__init__.py +3 -0
  87. pygeai/dbg/debugger.py +565 -70
  88. pygeai/evaluation/clients.py +2 -1
  89. pygeai/evaluation/dataset/clients.py +46 -44
  90. pygeai/evaluation/plan/clients.py +28 -26
  91. pygeai/evaluation/result/clients.py +38 -5
  92. pygeai/gam/clients.py +10 -25
  93. pygeai/health/clients.py +4 -7
  94. pygeai/lab/agents/clients.py +21 -54
  95. pygeai/lab/agents/endpoints.py +2 -0
  96. pygeai/lab/clients.py +1 -0
  97. pygeai/lab/models.py +3 -3
  98. pygeai/lab/processes/clients.py +45 -127
  99. pygeai/lab/strategies/clients.py +11 -25
  100. pygeai/lab/tools/clients.py +23 -67
  101. pygeai/lab/tools/endpoints.py +3 -0
  102. pygeai/migration/__init__.py +31 -0
  103. pygeai/migration/strategies.py +404 -155
  104. pygeai/migration/tools.py +170 -3
  105. pygeai/organization/clients.py +135 -51
  106. pygeai/organization/endpoints.py +6 -1
  107. pygeai/organization/limits/clients.py +32 -91
  108. pygeai/organization/managers.py +157 -1
  109. pygeai/organization/mappers.py +76 -2
  110. pygeai/organization/responses.py +25 -1
  111. pygeai/proxy/clients.py +4 -1
  112. pygeai/tests/admin/test_clients.py +16 -11
  113. pygeai/tests/assistants/rag/test_clients.py +35 -23
  114. pygeai/tests/assistants/test_clients.py +22 -15
  115. pygeai/tests/auth/test_clients.py +191 -7
  116. pygeai/tests/chat/test_clients.py +211 -1
  117. pygeai/tests/cli/commands/test_embeddings.py +32 -9
  118. pygeai/tests/cli/commands/test_evaluation.py +7 -0
  119. pygeai/tests/cli/commands/test_migrate.py +112 -243
  120. pygeai/tests/cli/test_error_handler.py +225 -0
  121. pygeai/tests/cli/test_geai_driver.py +154 -0
  122. pygeai/tests/cli/test_parsers.py +5 -5
  123. pygeai/tests/core/embeddings/test_clients.py +144 -0
  124. pygeai/tests/core/embeddings/test_managers.py +171 -0
  125. pygeai/tests/core/embeddings/test_mappers.py +142 -0
  126. pygeai/tests/core/feedback/test_clients.py +2 -0
  127. pygeai/tests/core/files/test_clients.py +1 -0
  128. pygeai/tests/core/llm/test_clients.py +14 -9
  129. pygeai/tests/core/plugins/test_clients.py +5 -3
  130. pygeai/tests/core/rerank/test_clients.py +1 -0
  131. pygeai/tests/core/secrets/test_clients.py +19 -13
  132. pygeai/tests/dbg/test_debugger.py +453 -75
  133. pygeai/tests/evaluation/dataset/test_clients.py +3 -1
  134. pygeai/tests/evaluation/plan/test_clients.py +4 -2
  135. pygeai/tests/evaluation/result/test_clients.py +7 -5
  136. pygeai/tests/gam/test_clients.py +1 -1
  137. pygeai/tests/health/test_clients.py +1 -0
  138. pygeai/tests/lab/agents/test_clients.py +9 -0
  139. pygeai/tests/lab/processes/test_clients.py +36 -0
  140. pygeai/tests/lab/processes/test_mappers.py +3 -0
  141. pygeai/tests/lab/strategies/test_clients.py +14 -9
  142. pygeai/tests/migration/test_strategies.py +45 -218
  143. pygeai/tests/migration/test_tools.py +133 -9
  144. pygeai/tests/organization/limits/test_clients.py +17 -0
  145. pygeai/tests/organization/test_clients.py +206 -1
  146. pygeai/tests/organization/test_managers.py +122 -1
  147. pygeai/tests/proxy/test_clients.py +2 -0
  148. pygeai/tests/proxy/test_integration.py +1 -0
  149. pygeai/tests/snippets/auth/__init__.py +0 -0
  150. pygeai/tests/snippets/chat/chat_completion_with_reasoning_effort.py +18 -0
  151. pygeai/tests/snippets/chat/get_response.py +15 -0
  152. pygeai/tests/snippets/chat/get_response_streaming.py +20 -0
  153. pygeai/tests/snippets/chat/get_response_with_files.py +16 -0
  154. pygeai/tests/snippets/chat/get_response_with_tools.py +36 -0
  155. pygeai/tests/snippets/dbg/__init__.py +0 -0
  156. pygeai/tests/snippets/dbg/basic_debugging.py +32 -0
  157. pygeai/tests/snippets/dbg/breakpoint_management.py +48 -0
  158. pygeai/tests/snippets/dbg/stack_navigation.py +45 -0
  159. pygeai/tests/snippets/dbg/stepping_example.py +40 -0
  160. pygeai/tests/snippets/embeddings/cache_example.py +31 -0
  161. pygeai/tests/snippets/embeddings/cohere_example.py +41 -0
  162. pygeai/tests/snippets/embeddings/openai_base64_example.py +27 -0
  163. pygeai/tests/snippets/embeddings/openai_example.py +30 -0
  164. pygeai/tests/snippets/embeddings/similarity_example.py +42 -0
  165. pygeai/tests/snippets/evaluation/dataset/__init__.py +0 -0
  166. pygeai/tests/snippets/evaluation/dataset/complete_workflow_example.py +195 -0
  167. pygeai/tests/snippets/evaluation/dataset/create_dataset.py +26 -0
  168. pygeai/tests/snippets/evaluation/dataset/create_dataset_from_file.py +11 -0
  169. pygeai/tests/snippets/evaluation/dataset/create_dataset_row.py +17 -0
  170. pygeai/tests/snippets/evaluation/dataset/create_expected_source.py +18 -0
  171. pygeai/tests/snippets/evaluation/dataset/create_filter_variable.py +19 -0
  172. pygeai/tests/snippets/evaluation/dataset/delete_dataset.py +9 -0
  173. pygeai/tests/snippets/evaluation/dataset/delete_dataset_row.py +10 -0
  174. pygeai/tests/snippets/evaluation/dataset/delete_expected_source.py +15 -0
  175. pygeai/tests/snippets/evaluation/dataset/delete_filter_variable.py +15 -0
  176. pygeai/tests/snippets/evaluation/dataset/get_dataset.py +9 -0
  177. pygeai/tests/snippets/evaluation/dataset/get_dataset_row.py +10 -0
  178. pygeai/tests/snippets/evaluation/dataset/get_expected_source.py +15 -0
  179. pygeai/tests/snippets/evaluation/dataset/get_filter_variable.py +15 -0
  180. pygeai/tests/snippets/evaluation/dataset/list_dataset_rows.py +9 -0
  181. pygeai/tests/snippets/evaluation/dataset/list_datasets.py +6 -0
  182. pygeai/tests/snippets/evaluation/dataset/list_expected_sources.py +10 -0
  183. pygeai/tests/snippets/evaluation/dataset/list_filter_variables.py +10 -0
  184. pygeai/tests/snippets/evaluation/dataset/update_dataset.py +15 -0
  185. pygeai/tests/snippets/evaluation/dataset/update_dataset_row.py +20 -0
  186. pygeai/tests/snippets/evaluation/dataset/update_expected_source.py +18 -0
  187. pygeai/tests/snippets/evaluation/dataset/update_filter_variable.py +19 -0
  188. pygeai/tests/snippets/evaluation/dataset/upload_dataset_rows_file.py +10 -0
  189. pygeai/tests/snippets/evaluation/plan/__init__.py +0 -0
  190. pygeai/tests/snippets/evaluation/plan/add_plan_system_metric.py +13 -0
  191. pygeai/tests/snippets/evaluation/plan/complete_workflow_example.py +136 -0
  192. pygeai/tests/snippets/evaluation/plan/create_evaluation_plan.py +24 -0
  193. pygeai/tests/snippets/evaluation/plan/create_rag_evaluation_plan.py +22 -0
  194. pygeai/tests/snippets/evaluation/plan/delete_evaluation_plan.py +9 -0
  195. pygeai/tests/snippets/evaluation/plan/delete_plan_system_metric.py +13 -0
  196. pygeai/tests/snippets/evaluation/plan/execute_evaluation_plan.py +11 -0
  197. pygeai/tests/snippets/evaluation/plan/get_evaluation_plan.py +9 -0
  198. pygeai/tests/snippets/evaluation/plan/get_plan_system_metric.py +13 -0
  199. pygeai/tests/snippets/evaluation/plan/get_system_metric.py +9 -0
  200. pygeai/tests/snippets/evaluation/plan/list_evaluation_plans.py +7 -0
  201. pygeai/tests/snippets/evaluation/plan/list_plan_system_metrics.py +9 -0
  202. pygeai/tests/snippets/evaluation/plan/list_system_metrics.py +7 -0
  203. pygeai/tests/snippets/evaluation/plan/update_evaluation_plan.py +22 -0
  204. pygeai/tests/snippets/evaluation/plan/update_plan_system_metric.py +14 -0
  205. pygeai/tests/snippets/evaluation/result/__init__.py +0 -0
  206. pygeai/tests/snippets/evaluation/result/complete_workflow_example.py +150 -0
  207. pygeai/tests/snippets/evaluation/result/get_evaluation_result.py +26 -0
  208. pygeai/tests/snippets/evaluation/result/list_evaluation_results.py +17 -0
  209. pygeai/tests/snippets/migrate/__init__.py +45 -0
  210. pygeai/tests/snippets/migrate/agent_migration.py +110 -0
  211. pygeai/tests/snippets/migrate/assistant_migration.py +64 -0
  212. pygeai/tests/snippets/migrate/orchestrator_examples.py +179 -0
  213. pygeai/tests/snippets/migrate/process_migration.py +64 -0
  214. pygeai/tests/snippets/migrate/project_migration.py +42 -0
  215. pygeai/tests/snippets/migrate/tool_migration.py +64 -0
  216. pygeai/tests/snippets/organization/create_project.py +2 -2
  217. pygeai/tests/snippets/organization/get_memberships.py +12 -0
  218. pygeai/tests/snippets/organization/get_organization_members.py +6 -0
  219. pygeai/tests/snippets/organization/get_project_members.py +6 -0
  220. pygeai/tests/snippets/organization/get_project_memberships.py +12 -0
  221. pygeai/tests/snippets/organization/get_project_roles.py +6 -0
  222. {pygeai-0.6.0b6.dist-info → pygeai-0.6.0b10.dist-info}/METADATA +1 -1
  223. {pygeai-0.6.0b6.dist-info → pygeai-0.6.0b10.dist-info}/RECORD +227 -124
  224. {pygeai-0.6.0b6.dist-info → pygeai-0.6.0b10.dist-info}/WHEEL +0 -0
  225. {pygeai-0.6.0b6.dist-info → pygeai-0.6.0b10.dist-info}/entry_points.txt +0 -0
  226. {pygeai-0.6.0b6.dist-info → pygeai-0.6.0b10.dist-info}/licenses/LICENSE +0 -0
  227. {pygeai-0.6.0b6.dist-info → pygeai-0.6.0b10.dist-info}/top_level.txt +0 -0
pygeai/auth/clients.py CHANGED
@@ -1,9 +1,10 @@
1
- from json import JSONDecodeError
2
-
3
1
  from pygeai import logger
4
- from pygeai.auth.endpoints import GET_USER_PROFILE_INFO, GET_OAUTH2_ACCESS_TOKEN
2
+ from pygeai.auth.endpoints import GET_USER_PROFILE_INFO, GET_OAUTH2_ACCESS_TOKEN, \
3
+ CREATE_PROJECT_API_TOKEN_V2, DELETE_PROJECT_API_TOKEN_V2, UPDATE_PROJECT_API_TOKEN_V2, GET_PROJECT_API_TOKEN_V2
5
4
  from pygeai.core.base.clients import BaseClient
6
- from pygeai.core.common.exceptions import InvalidAPIResponseException
5
+ from pygeai.core.common.exceptions import InvalidAPIResponseException, APIResponseError
6
+ from pygeai.core.utils.validators import validate_status_code
7
+ from pygeai.core.utils.parsers import parse_json_response
7
8
 
8
9
 
9
10
  class AuthClient(BaseClient):
@@ -33,11 +34,8 @@ class AuthClient(BaseClient):
33
34
  "password": password
34
35
  }
35
36
  )
36
- try:
37
- return response.json()
38
- except JSONDecodeError as e:
39
- logger.error(f"Unable to obtain Oauth2 access token: JSON parsing error (status {response.status_code}): {e}. Response: {response.text}")
40
- raise InvalidAPIResponseException(f"Unable to obtain Oauth2 access token: {response.text}")
37
+ validate_status_code(response)
38
+ return parse_json_response(response, "obtain Oauth2 access token")
41
39
 
42
40
  def get_user_profile_information(self, access_token: str) -> dict:
43
41
  """
@@ -48,8 +46,84 @@ class AuthClient(BaseClient):
48
46
  """
49
47
  self.api_service.token = access_token
50
48
  response = self.api_service.get(endpoint=GET_USER_PROFILE_INFO)
51
- try:
52
- return response.json()
53
- except JSONDecodeError as e:
54
- logger.error(f"Unable to retrieve user profile information: JSON parsing error (status {response.status_code}): {e}. Response: {response.text}")
55
- raise InvalidAPIResponseException(f"Unable to retrieve user profile information: {response.text}")
49
+ validate_status_code(response)
50
+ return parse_json_response(response, "retrieve user profile information")
51
+
52
+ def create_project_api_token(
53
+ self,
54
+ project_id: str,
55
+ name: str,
56
+ description: str = None
57
+ ) -> dict:
58
+ """
59
+ Creates a new API token for a project.
60
+
61
+ :param project_id: str - The project identifier (required). Will be sent as header.
62
+ :param name: str - The name of the API token (required).
63
+ :param description: str - A description of the API token (optional).
64
+ :return: dict - The API response containing the created API token details in JSON format.
65
+ """
66
+ headers = {"project-id": project_id}
67
+ data = {"name": name}
68
+ if description:
69
+ data["description"] = description
70
+
71
+ response = self.api_service.post(
72
+ endpoint=CREATE_PROJECT_API_TOKEN_V2,
73
+ data=data,
74
+ headers=headers
75
+ )
76
+ validate_status_code(response)
77
+ return parse_json_response(response, "create project API token")
78
+
79
+ def delete_project_api_token(self, api_token_id: str) -> dict:
80
+ """
81
+ Revokes an API token by setting its status to "revoked".
82
+
83
+ :param api_token_id: str - The unique identifier of the API token to delete (required).
84
+ :return: dict - The API response confirming the deletion, in JSON format.
85
+ """
86
+ endpoint = DELETE_PROJECT_API_TOKEN_V2.format(id=api_token_id)
87
+ response = self.api_service.delete(endpoint=endpoint)
88
+ validate_status_code(response)
89
+ return parse_json_response(response, "delete project API token", api_token_id=api_token_id)
90
+
91
+ def update_project_api_token(
92
+ self,
93
+ api_token_id: str,
94
+ description: str = None,
95
+ status: str = None
96
+ ) -> dict:
97
+ """
98
+ Updates an existing API token's description and/or status.
99
+
100
+ :param api_token_id: str - The unique identifier of the API token to update (required).
101
+ :param description: str - A new description for the API token (optional).
102
+ :param status: str - The new status for the API token: 'active' or 'blocked' (optional).
103
+ :return: dict - The API response containing the update result messages in JSON format.
104
+ """
105
+ endpoint = UPDATE_PROJECT_API_TOKEN_V2.format(id=api_token_id)
106
+ data = {}
107
+ if description is not None:
108
+ data["description"] = description
109
+ if status is not None:
110
+ data["status"] = status
111
+
112
+ response = self.api_service.put(
113
+ endpoint=endpoint,
114
+ data=data
115
+ )
116
+ validate_status_code(response)
117
+ return parse_json_response(response, "update project API token", api_token_id=api_token_id)
118
+
119
+ def get_project_api_token(self, api_token_id: str) -> dict:
120
+ """
121
+ Retrieves data for a specific project API token.
122
+
123
+ :param api_token_id: str - The unique identifier of the API token (required).
124
+ :return: dict - The API response containing the API token details in JSON format.
125
+ """
126
+ endpoint = GET_PROJECT_API_TOKEN_V2.format(id=api_token_id)
127
+ response = self.api_service.get(endpoint=endpoint)
128
+ validate_status_code(response)
129
+ return parse_json_response(response, "get project API token", api_token_id=api_token_id)
pygeai/auth/endpoints.py CHANGED
@@ -1,2 +1,6 @@
1
1
  GET_OAUTH2_ACCESS_TOKEN = "/oauth/access_token" # POST -> Obtain an OAuth 2.0 access token
2
2
  GET_USER_PROFILE_INFO = "/openid/userinfo" # GET -> Retrieve user profile information
3
+ CREATE_PROJECT_API_TOKEN_V2 = "v2/projects/tokens" # POST -> Create a new API token for a project
4
+ DELETE_PROJECT_API_TOKEN_V2 = "v2/projects/tokens/{id}" # DELETE -> Revoke an API token
5
+ UPDATE_PROJECT_API_TOKEN_V2 = "v2/projects/tokens/{id}" # PUT -> Update an API token
6
+ GET_PROJECT_API_TOKEN_V2 = "v2/projects/tokens/{id}" # GET -> Get data of a specific project API token
pygeai/chat/clients.py CHANGED
@@ -1,11 +1,14 @@
1
1
  import json
2
2
  from json import JSONDecodeError
3
+ from pathlib import Path
3
4
  from typing import List, Dict, Optional, Union, Generator
4
5
 
5
6
  from pygeai import logger
6
- from pygeai.chat.endpoints import CHAT_V1, GENERATE_IMAGE_V1
7
+ from pygeai.chat.endpoints import CHAT_V1, GENERATE_IMAGE_V1, RESPONSES_V1
7
8
  from pygeai.core.base.clients import BaseClient
8
9
  from pygeai.core.common.exceptions import InvalidAPIResponseException
10
+ from pygeai.core.utils.validators import validate_status_code
11
+ from pygeai.core.utils.parsers import parse_json_response
9
12
 
10
13
 
11
14
  class ChatClient(BaseClient):
@@ -14,8 +17,8 @@ class ChatClient(BaseClient):
14
17
  response = self.api_service.post(
15
18
  endpoint=CHAT_V1
16
19
  )
17
- result = response.json()
18
- return result
20
+ validate_status_code(response)
21
+ return parse_json_response(response, "chat")
19
22
 
20
23
  def chat_completion(
21
24
  self,
@@ -39,7 +42,8 @@ class ChatClient(BaseClient):
39
42
  stream_options: Optional[Dict] = None,
40
43
  store: Optional[bool] = None,
41
44
  metadata: Optional[Dict] = None,
42
- user: Optional[str] = None
45
+ user: Optional[str] = None,
46
+ reasoning_effort: Optional[str] = None
43
47
  ) -> Union[dict, str, Generator[str, None, None]]:
44
48
  """
45
49
  Generates a chat completion response using the specified model and parameters.
@@ -49,8 +53,8 @@ class ChatClient(BaseClient):
49
53
  :param messages: List[Dict[str, str]] - A list of messages to include in the chat completion. Each message should be a dictionary with
50
54
  the following structure:
51
55
  {
52
- "role": "string", # Possible values: "user", "system", "assistant", or others supported by the model
53
- "content": "string" # The content of the message
56
+ "role": "string",
57
+ "content": "string"
54
58
  } (Required)
55
59
  :param stream: bool - Whether the response should be streamed. Possible values:
56
60
  - False: Returns the complete response as a dictionary or string (default).
@@ -77,6 +81,9 @@ class ChatClient(BaseClient):
77
81
  :param store: Optional[bool] - Whether to store the output for model distillation or evals. (Optional)
78
82
  :param metadata: Optional[Dict] - Up to 16 key-value pairs to attach to the object. (Optional)
79
83
  :param user: Optional[str] - A unique identifier for the end-user to monitor abuse. (Optional)
84
+ :param reasoning_effort: Optional[str] - Controls the depth of reasoning applied by supported models.
85
+ Possible values: "low", "medium", "high". Supported by OpenAI models from version 5,
86
+ Claude models from version 4.1, and Gemini models from version 2.0. (Optional)
80
87
  :return: Union[dict, str, Generator[str, None, None]] - For non-streaming (stream=False), returns a dictionary containing the chat completion
81
88
  result or a string if JSON decoding fails. For streaming (stream=True), returns a generator yielding content strings extracted from the
82
89
  streaming response chunks.
@@ -86,19 +93,16 @@ class ChatClient(BaseClient):
86
93
  'messages': messages,
87
94
  'stream': stream
88
95
  }
89
- if temperature:
96
+ if temperature is not None:
90
97
  data['temperature'] = temperature
91
98
 
92
99
  if max_tokens:
93
100
  data['max_completion_tokens'] = max_tokens
94
101
 
95
- if thread_id:
96
- data['threadId'] = thread_id
97
-
98
- if frequency_penalty:
102
+ if frequency_penalty is not None:
99
103
  data['frequency_penalty'] = frequency_penalty
100
104
 
101
- if presence_penalty:
105
+ if presence_penalty is not None:
102
106
  data['presence_penalty'] = presence_penalty
103
107
 
104
108
  if variables is not None and any(variables):
@@ -140,6 +144,9 @@ class ChatClient(BaseClient):
140
144
  if user is not None:
141
145
  data['user'] = user
142
146
 
147
+ if reasoning_effort is not None:
148
+ data['reasoning_effort'] = reasoning_effort
149
+
143
150
  headers = {}
144
151
  if thread_id:
145
152
  headers["saia-conversation-id"] = thread_id
@@ -159,12 +166,13 @@ class ChatClient(BaseClient):
159
166
  data=data,
160
167
  headers=headers
161
168
  )
162
- try:
163
- result = response.json()
164
- logger.debug(f"Chat completion result: {result}")
165
- return result
166
- except JSONDecodeError as e:
167
- raise InvalidAPIResponseException(f"Unable to process chat request: {response.text}")
169
+ validate_status_code(response)
170
+
171
+ result = parse_json_response(response, "process chat request")
172
+
173
+ logger.debug(f"Chat completion result: {result}")
174
+
175
+ return result
168
176
 
169
177
  def stream_chat_generator(self, response) -> Generator[str, None, None]:
170
178
  """
@@ -194,6 +202,34 @@ class ChatClient(BaseClient):
194
202
  except Exception as e:
195
203
  raise InvalidAPIResponseException(f"Unable to process streaming chat response: {e}")
196
204
 
205
+ def stream_response_generator(self, response) -> Generator[str, None, None]:
206
+ """
207
+ Processes a streaming response from the Responses API and yields content strings.
208
+
209
+ :param response: The streaming response from the API.
210
+ :return: Generator[str, None, None] - Yields content strings extracted from streaming chunks.
211
+ """
212
+ try:
213
+ for line in response:
214
+ if line.startswith("data:"):
215
+ chunk = line[5:].strip()
216
+ if chunk == "[DONE]":
217
+ break
218
+ try:
219
+ json_data = json.loads(chunk)
220
+ if (
221
+ json_data.get("choices")
222
+ and len(json_data["choices"]) > 0
223
+ and "delta" in json_data["choices"][0]
224
+ and "content" in json_data["choices"][0]["delta"]
225
+ ):
226
+ content = json_data["choices"][0]["delta"]["content"]
227
+ yield content
228
+ except JSONDecodeError as e:
229
+ continue
230
+ except Exception as e:
231
+ raise InvalidAPIResponseException(f"Unable to process streaming response: {e}")
232
+
197
233
  def generate_image(
198
234
  self,
199
235
  model: str,
@@ -211,7 +247,7 @@ class ChatClient(BaseClient):
211
247
  :param n: int - Number of images to generate (1-10, depending on the model). (Required)
212
248
  :param quality: str - Rendering quality, e.g., "high". (Required)
213
249
  :param size: str - Image dimensions, e.g., "1024x1024". (Required)
214
- :param aspect_ratio: Optional[str] - Relationship between images width and height, e.g., "1:1", "9:16", "16:9", "3:4", "4:3". (Optional)
250
+ :param aspect_ratio: Optional[str] - Relationship between image's width and height, e.g., "1:1", "9:16", "16:9", "3:4", "4:3". (Optional)
215
251
  :return: dict - The API response containing the generated image data.
216
252
  :raises InvalidAPIResponseException: If the API response cannot be processed.
217
253
  """
@@ -233,9 +269,140 @@ class ChatClient(BaseClient):
233
269
  data=data
234
270
  )
235
271
 
236
- try:
237
- result = response.json()
238
- logger.debug(f"Image generation result: {result}")
239
- return result
240
- except JSONDecodeError as e:
241
- raise InvalidAPIResponseException(f"Unable to process image generation request: {response.text}")
272
+ validate_status_code(response)
273
+
274
+ result = parse_json_response(response, "generate image")
275
+ logger.debug(f"Image generation result: {result}")
276
+ return result
277
+
278
+ def get_response(
279
+ self,
280
+ model: str,
281
+ input: str,
282
+ files: Optional[List[str]] = None,
283
+ tools: Optional[List[Dict]] = None,
284
+ tool_choice: Optional[Union[str, Dict]] = None,
285
+ temperature: Optional[float] = None,
286
+ max_output_tokens: Optional[int] = None,
287
+ top_p: Optional[float] = None,
288
+ metadata: Optional[Dict] = None,
289
+ user: Optional[str] = None,
290
+ instructions: Optional[str] = None,
291
+ reasoning: Optional[Dict] = None,
292
+ truncation: Optional[str] = None,
293
+ parallel_tool_calls: Optional[bool] = None,
294
+ store: Optional[bool] = None,
295
+ stream: bool = False
296
+ ) -> Union[dict, str, Generator[str, None, None]]:
297
+ """
298
+ Generates a response using the Responses API with support for images and PDF files.
299
+
300
+ :param model: str - The model specification, e.g., "openai/o1-pro". (Required)
301
+ :param input: str - The user input text. (Required)
302
+ :param files: Optional[List[str]] - List of file paths (images or PDFs) to include in the request. (Optional)
303
+ :param tools: Optional[List[Dict]] - A list of tools (e.g., functions) the model may call. (Optional)
304
+ :param tool_choice: Optional[Union[str, Dict]] - Controls which tool is called (e.g., "none", "auto", or specific tool). (Optional)
305
+ :param temperature: Optional[float] - Controls the randomness of the response. (Optional)
306
+ :param max_output_tokens: Optional[int] - The maximum number of tokens to generate in the response. (Optional)
307
+ :param top_p: Optional[float] - Nucleus sampling parameter. (Optional)
308
+ :param metadata: Optional[Dict] - Up to 16 key-value pairs to attach to the object. (Optional)
309
+ :param user: Optional[str] - A unique identifier for the end-user. (Optional)
310
+ :param instructions: Optional[str] - Additional instructions for the model. (Optional)
311
+ :param reasoning: Optional[Dict] - Reasoning configuration, e.g., {"effort": "medium"}. (Optional)
312
+ :param truncation: Optional[str] - Truncation strategy, e.g., "disabled". (Optional)
313
+ :param parallel_tool_calls: Optional[bool] - Whether to enable parallel tool calls. (Optional)
314
+ :param store: Optional[bool] - Whether to store the output. (Optional)
315
+ :param stream: bool - Whether the response should be streamed. Possible values:
316
+ - False: Returns the complete response as a dictionary or string (default).
317
+ - True: Returns a generator yielding content strings as they are received. (Optional)
318
+ :return: Union[dict, str, Generator[str, None, None]] - For non-streaming (stream=False), returns a dictionary containing the response
319
+ result or a string if JSON decoding fails. For streaming (stream=True), returns a generator yielding content strings extracted from the
320
+ streaming response chunks.
321
+ :raises InvalidAPIResponseException: If the API response cannot be processed.
322
+ """
323
+ data = {
324
+ 'model': model,
325
+ 'input': input,
326
+ 'stream': stream
327
+ }
328
+
329
+ if temperature is not None:
330
+ data['temperature'] = temperature
331
+
332
+ if max_output_tokens is not None:
333
+ data['max_output_tokens'] = max_output_tokens
334
+
335
+ if top_p is not None:
336
+ data['top_p'] = top_p
337
+
338
+ if tools is not None:
339
+ data['tools'] = tools
340
+
341
+ if tool_choice is not None:
342
+ data['tool_choice'] = tool_choice
343
+
344
+ if metadata is not None:
345
+ data['metadata'] = metadata
346
+
347
+ if user is not None:
348
+ data['user'] = user
349
+
350
+ if instructions is not None:
351
+ data['instructions'] = instructions
352
+
353
+ if reasoning is not None:
354
+ data['reasoning'] = reasoning
355
+
356
+ if truncation is not None:
357
+ data['truncation'] = truncation
358
+
359
+ if parallel_tool_calls is not None:
360
+ data['parallel_tool_calls'] = parallel_tool_calls
361
+
362
+ if store is not None:
363
+ data['store'] = store
364
+
365
+ logger.debug(f"Generating response with data: {data}")
366
+
367
+ if files:
368
+ if stream:
369
+ raise InvalidAPIResponseException("Streaming is not supported when uploading files")
370
+
371
+ file_handles = []
372
+ try:
373
+ files_dict = {}
374
+ for idx, file_path in enumerate(files):
375
+ path = Path(file_path)
376
+ if not path.is_file():
377
+ raise FileNotFoundError(f"File not found: {file_path}")
378
+
379
+ file_handle = path.open("rb")
380
+ file_handles.append(file_handle)
381
+ files_dict[f"file{idx}"] = file_handle
382
+
383
+ response = self.api_service.post_files_multipart(
384
+ endpoint=RESPONSES_V1,
385
+ data=data,
386
+ files=files_dict
387
+ )
388
+ finally:
389
+ for fh in file_handles:
390
+ fh.close()
391
+ else:
392
+ if stream:
393
+ response = self.api_service.stream_post(
394
+ endpoint=RESPONSES_V1,
395
+ data=data
396
+ )
397
+ return self.stream_response_generator(response)
398
+ else:
399
+ response = self.api_service.post(
400
+ endpoint=RESPONSES_V1,
401
+ data=data
402
+ )
403
+
404
+ validate_status_code(response)
405
+ result = parse_json_response(response, "get response")
406
+ logger.debug(f"Response result: {result}")
407
+
408
+ return result
pygeai/chat/endpoints.py CHANGED
@@ -1,3 +1,4 @@
1
1
  CHAT_V1 = "/chat" # POST Chat endpoint
2
2
  CHAT_COMPLETION_V1 = "/chat/completion" # POST Chat completion endpoint
3
- GENERATE_IMAGE_V1 = "/images" # POST Generate image
3
+ GENERATE_IMAGE_V1 = "/images" # POST Generate image
4
+ RESPONSES_V1 = "/responses" # POST Responses API endpoint
@@ -92,6 +92,146 @@ get_user_profile_information_options = [
92
92
  ]
93
93
 
94
94
 
95
+ def create_project_api_token(option_list: list):
96
+ project_id = None
97
+ name = None
98
+ description = None
99
+ for option_flag, option_arg in option_list:
100
+ if option_flag.name == "project_id":
101
+ project_id = option_arg
102
+ if option_flag.name == "name":
103
+ name = option_arg
104
+ if option_flag.name == "description":
105
+ description = option_arg
106
+
107
+ if not (project_id and name):
108
+ raise MissingRequirementException("Cannot create project API token without project-id and name")
109
+
110
+ client = AuthClient()
111
+ result = client.create_project_api_token(
112
+ project_id=project_id,
113
+ name=name,
114
+ description=description
115
+ )
116
+ Console.write_stdout(f"Project API token created: \n{result}")
117
+
118
+
119
+ create_project_api_token_options = [
120
+ Option(
121
+ "project_id",
122
+ ["--project-id", "--pid"],
123
+ "The project identifier (required).",
124
+ True
125
+ ),
126
+ Option(
127
+ "name",
128
+ ["--name", "-n"],
129
+ "The name of the API token (required).",
130
+ True
131
+ ),
132
+ Option(
133
+ "description",
134
+ ["--description", "-d"],
135
+ "A description of the API token (optional).",
136
+ True
137
+ ),
138
+ ]
139
+
140
+
141
+ def delete_project_api_token(option_list: list):
142
+ api_token_id = None
143
+ for option_flag, option_arg in option_list:
144
+ if option_flag.name == "api_token_id":
145
+ api_token_id = option_arg
146
+
147
+ if not api_token_id:
148
+ raise MissingRequirementException("Cannot delete project API token without api-token-id")
149
+
150
+ client = AuthClient()
151
+ result = client.delete_project_api_token(api_token_id=api_token_id)
152
+ Console.write_stdout(f"Project API token deleted: \n{result}")
153
+
154
+
155
+ delete_project_api_token_options = [
156
+ Option(
157
+ "api_token_id",
158
+ ["--api-token-id", "--tid"],
159
+ "The unique identifier of the API token to delete (required).",
160
+ True
161
+ ),
162
+ ]
163
+
164
+
165
+ def update_project_api_token(option_list: list):
166
+ api_token_id = None
167
+ description = None
168
+ status = None
169
+ for option_flag, option_arg in option_list:
170
+ if option_flag.name == "api_token_id":
171
+ api_token_id = option_arg
172
+ if option_flag.name == "description":
173
+ description = option_arg
174
+ if option_flag.name == "status":
175
+ status = option_arg
176
+
177
+ if not api_token_id:
178
+ raise MissingRequirementException("Cannot update project API token without api-token-id")
179
+
180
+ client = AuthClient()
181
+ result = client.update_project_api_token(
182
+ api_token_id=api_token_id,
183
+ description=description,
184
+ status=status
185
+ )
186
+ Console.write_stdout(f"Project API token updated: \n{result}")
187
+
188
+
189
+ update_project_api_token_options = [
190
+ Option(
191
+ "api_token_id",
192
+ ["--api-token-id", "--tid"],
193
+ "The unique identifier of the API token to update (required).",
194
+ True
195
+ ),
196
+ Option(
197
+ "description",
198
+ ["--description", "-d"],
199
+ "A new description for the API token (optional).",
200
+ True
201
+ ),
202
+ Option(
203
+ "status",
204
+ ["--status"],
205
+ "The new status for the API token: 'active' or 'blocked' (optional).",
206
+ True
207
+ ),
208
+ ]
209
+
210
+
211
+ def get_project_api_token(option_list: list):
212
+ api_token_id = None
213
+ for option_flag, option_arg in option_list:
214
+ if option_flag.name == "api_token_id":
215
+ api_token_id = option_arg
216
+
217
+ if not api_token_id:
218
+ raise MissingRequirementException("Cannot get project API token without api-token-id")
219
+
220
+ client = AuthClient()
221
+ result = client.get_project_api_token(api_token_id=api_token_id)
222
+ Console.write_stdout(f"Project API token details: \n{result}")
223
+
224
+
225
+ get_project_api_token_options = [
226
+ Option(
227
+ "api_token_id",
228
+ ["--api-token-id", "--tid"],
229
+ "The unique identifier of the API token (required).",
230
+ True
231
+ ),
232
+ ]
233
+
234
+
95
235
  auth_commands = [
96
236
  Command(
97
237
  "help",
@@ -112,12 +252,48 @@ auth_commands = [
112
252
  get_oauth2_access_token_options
113
253
  ),
114
254
  Command(
115
- "get_user_profile_information_options",
255
+ "get_user_profile_information",
116
256
  ["get-user-information", "get-user-info", "gui"],
117
257
  "Retrieve user profile information",
118
- get_user_profile_information_options,
258
+ get_user_profile_information,
119
259
  ArgumentsEnum.REQUIRED,
120
260
  [],
121
261
  get_user_profile_information_options
122
262
  ),
263
+ Command(
264
+ "create_project_api_token",
265
+ ["create-project-api-token", "create-api-token", "cat"],
266
+ "Create a new API token for a project",
267
+ create_project_api_token,
268
+ ArgumentsEnum.REQUIRED,
269
+ [],
270
+ create_project_api_token_options
271
+ ),
272
+ Command(
273
+ "delete_project_api_token",
274
+ ["delete-project-api-token", "delete-api-token", "dat"],
275
+ "Revoke an API token",
276
+ delete_project_api_token,
277
+ ArgumentsEnum.REQUIRED,
278
+ [],
279
+ delete_project_api_token_options
280
+ ),
281
+ Command(
282
+ "update_project_api_token",
283
+ ["update-project-api-token", "update-api-token", "uat"],
284
+ "Update an existing API token",
285
+ update_project_api_token,
286
+ ArgumentsEnum.REQUIRED,
287
+ [],
288
+ update_project_api_token_options
289
+ ),
290
+ Command(
291
+ "get_project_api_token",
292
+ ["get-project-api-token", "get-api-token", "gat"],
293
+ "Get data of a specific project API token",
294
+ get_project_api_token,
295
+ ArgumentsEnum.REQUIRED,
296
+ [],
297
+ get_project_api_token_options
298
+ ),
123
299
  ]