pygeai 0.4.0b3__py3-none-any.whl → 0.4.0b5__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.
@@ -110,8 +110,8 @@ class AgenticProcessMapper:
110
110
  """
111
111
  return [
112
112
  Variable(
113
- key=var["key"],
114
- value=var["value"]
113
+ key=var.get("key"),
114
+ value=var.get("value")
115
115
  )
116
116
  for var in data
117
117
  ]
@@ -1,13 +1,13 @@
1
1
  from json import JSONDecodeError
2
2
 
3
3
  from pygeai import logger
4
- from pygeai.core.base.clients import BaseClient
5
4
  from pygeai.core.common.exceptions import InvalidAPIResponseException
5
+ from pygeai.lab.clients import AILabClient
6
6
  from pygeai.lab.strategies.endpoints import LIST_REASONING_STRATEGIES_V2, CREATE_REASONING_STRATEGY_V2, \
7
7
  UPDATE_REASONING_STRATEGY_V2, UPSERT_REASONING_STRATEGY_V2, GET_REASONING_STRATEGY_V2
8
8
 
9
9
 
10
- class ReasoningStrategyClient(BaseClient):
10
+ class ReasoningStrategyClient(AILabClient):
11
11
 
12
12
  def list_reasoning_strategies(
13
13
  self,
@@ -56,7 +56,6 @@ class ReasoningStrategyClient(BaseClient):
56
56
 
57
57
  def create_reasoning_strategy(
58
58
  self,
59
- project_id: str,
60
59
  name: str,
61
60
  system_prompt: str,
62
61
  access_scope: str = "public",
@@ -67,7 +66,6 @@ class ReasoningStrategyClient(BaseClient):
67
66
  """
68
67
  Creates a new reasoning strategy in the specified project.
69
68
 
70
- :param project_id: str - Unique identifier of the project.
71
69
  :param name: str - Name of the reasoning strategy.
72
70
  :param system_prompt: str - System prompt for the strategy.
73
71
  :param access_scope: str, optional - Access scope, "public" or "private" (default: "public").
@@ -83,7 +81,7 @@ class ReasoningStrategyClient(BaseClient):
83
81
 
84
82
  headers = {
85
83
  "Authorization": self.api_service.token,
86
- "ProjectId": project_id,
84
+ "ProjectId": self.project_id,
87
85
  "Content-Type": "application/json",
88
86
  "Accept": "application/json"
89
87
  }
@@ -103,14 +101,13 @@ class ReasoningStrategyClient(BaseClient):
103
101
  try:
104
102
  result = response.json()
105
103
  except JSONDecodeError as e:
106
- logger.error(f"Unable to create reasoning strategy for project {project_id}: JSON parsing error (status {response.status_code}): {e}. Response: {response.text}")
107
- raise InvalidAPIResponseException(f"Unable to create reasoning strategy for project {project_id}: {response.text}")
104
+ logger.error(f"Unable to create reasoning strategy for project {self.project_id}: JSON parsing error (status {response.status_code}): {e}. Response: {response.text}")
105
+ raise InvalidAPIResponseException(f"Unable to create reasoning strategy for project {self.project_id}: {response.text}")
108
106
 
109
107
  return result
110
108
 
111
109
  def update_reasoning_strategy(
112
110
  self,
113
- project_id: str,
114
111
  reasoning_strategy_id: str,
115
112
  name: str = None,
116
113
  system_prompt: str = None,
@@ -123,7 +120,6 @@ class ReasoningStrategyClient(BaseClient):
123
120
  """
124
121
  Updates or upserts a reasoning strategy in the specified project.
125
122
 
126
- :param project_id: str - Unique identifier of the project.
127
123
  :param reasoning_strategy_id: str - Unique identifier of the strategy.
128
124
  :param name: str, optional - Updated strategy name.
129
125
  :param system_prompt: str, optional - Updated system prompt.
@@ -169,7 +165,7 @@ class ReasoningStrategyClient(BaseClient):
169
165
 
170
166
  headers = {
171
167
  "Authorization": self.api_service.token,
172
- "ProjectId": project_id,
168
+ "ProjectId": self.project_id,
173
169
  "Content-Type": "application/json",
174
170
  "Accept": "application/json"
175
171
  }
@@ -181,21 +177,19 @@ class ReasoningStrategyClient(BaseClient):
181
177
  try:
182
178
  result = response.json()
183
179
  except JSONDecodeError as e:
184
- logger.error(f"Unable to update reasoning strategy {reasoning_strategy_id} in project {project_id}: JSON parsing error (status {response.status_code}): {e}. Response: {response.text}")
185
- raise InvalidAPIResponseException(f"Unable to update reasoning strategy {reasoning_strategy_id} in project {project_id}: {response.text}")
180
+ logger.error(f"Unable to update reasoning strategy {reasoning_strategy_id} in project {self.project_id}: JSON parsing error (status {response.status_code}): {e}. Response: {response.text}")
181
+ raise InvalidAPIResponseException(f"Unable to update reasoning strategy {reasoning_strategy_id} in project {self.project_id}: {response.text}")
186
182
 
187
183
  return result
188
184
 
189
185
  def get_reasoning_strategy(
190
186
  self,
191
- project_id: str,
192
187
  reasoning_strategy_id: str = None,
193
188
  reasoning_strategy_name: str = None
194
189
  ):
195
190
  """
196
191
  Retrieves a reasoning strategy by ID or name in the specified project.
197
192
 
198
- :param project_id: str - Unique identifier of the project.
199
193
  :param reasoning_strategy_id: str, optional - Unique identifier of the strategy.
200
194
  :param reasoning_strategy_name: str, optional - Name of the strategy.
201
195
  :return: dict - Strategy details.
@@ -210,7 +204,7 @@ class ReasoningStrategyClient(BaseClient):
210
204
 
211
205
  headers = {
212
206
  "Authorization": self.api_service.token,
213
- "ProjectId": project_id
207
+ "ProjectId": self.project_id
214
208
  }
215
209
 
216
210
  if reasoning_strategy_id:
@@ -225,8 +219,8 @@ class ReasoningStrategyClient(BaseClient):
225
219
  try:
226
220
  result = response.json()
227
221
  except JSONDecodeError as e:
228
- logger.error(f"Unable to retrieve reasoning strategy {reasoning_strategy_id or reasoning_strategy_name} for project {project_id}: JSON parsing error (status {response.status_code}): {e}. Response: {response.text}")
229
- raise InvalidAPIResponseException(f"Unable to retrieve reasoning strategy {reasoning_strategy_id or reasoning_strategy_name} for project {project_id}: {response.text}")
222
+ logger.error(f"Unable to retrieve reasoning strategy {reasoning_strategy_id or reasoning_strategy_name} for project {self.project_id}: JSON parsing error (status {response.status_code}): {e}. Response: {response.text}")
223
+ raise InvalidAPIResponseException(f"Unable to retrieve reasoning strategy {reasoning_strategy_id or reasoning_strategy_name} for project {self.project_id}: {response.text}")
230
224
 
231
225
  return result
232
226
 
@@ -2,18 +2,17 @@ import json
2
2
  from json import JSONDecodeError
3
3
 
4
4
  from pygeai import logger
5
- from pygeai.core.base.clients import BaseClient
6
5
  from pygeai.core.common.exceptions import InvalidAPIResponseException, MissingRequirementException
7
6
  from pygeai.lab.constants import VALID_SCOPES, VALID_ACCESS_SCOPES, VALID_REPORT_EVENTS
8
7
  from pygeai.lab.tools.endpoints import CREATE_TOOL_V2, LIST_TOOLS_V2, GET_TOOL_V2, UPDATE_TOOL_V2, UPSERT_TOOL_V2, \
9
8
  PUBLISH_TOOL_REVISION_V2, GET_PARAMETER_V2, SET_PARAMETER_V2, DELETE_TOOL_V2, EXPORT_TOOL_V2
9
+ from pygeai.lab.clients import AILabClient
10
10
 
11
11
 
12
- class ToolClient(BaseClient):
12
+ class ToolClient(AILabClient):
13
13
 
14
14
  def create_tool(
15
15
  self,
16
- project_id: str,
17
16
  name: str,
18
17
  description: str = None,
19
18
  scope: str = None,
@@ -29,7 +28,6 @@ class ToolClient(BaseClient):
29
28
  """
30
29
  Creates a new tool in the specified project.
31
30
 
32
- :param project_id: str - Unique identifier of the project where the tool will be created.
33
31
  :param name: str - Name of the tool. Must be non-empty, unique within the project, and exclude ':' or '/'.
34
32
  :param description: str - Description of the tool's purpose, helping agents decide when to use it. Optional.
35
33
  :param scope: str - Scope of the tool, either 'builtin', 'external', or 'api'. Optional.
@@ -108,7 +106,7 @@ class ToolClient(BaseClient):
108
106
  "Accept": "application/json",
109
107
  "Content-Type": "application/json",
110
108
  "Authorization": self.api_service.token,
111
- "ProjectId": project_id
109
+ "ProjectId": self.project_id
112
110
  }
113
111
 
114
112
  response = self.api_service.post(
@@ -120,14 +118,13 @@ class ToolClient(BaseClient):
120
118
  try:
121
119
  result = response.json()
122
120
  except JSONDecodeError as e:
123
- logger.error(f"Unable to create tool for project {project_id}: JSON parsing error (status {response.status_code}): {e}. Response: {response.text}")
124
- raise InvalidAPIResponseException(f"Unable to create tool for project {project_id}: {response.text}")
121
+ logger.error(f"Unable to create tool for project {self.project_id}: JSON parsing error (status {response.status_code}): {e}. Response: {response.text}")
122
+ raise InvalidAPIResponseException(f"Unable to create tool for project {self.project_id}: {response.text}")
125
123
 
126
124
  return result
127
125
 
128
126
  def list_tools(
129
127
  self,
130
- project_id: str,
131
128
  id: str = "",
132
129
  count: str = "100",
133
130
  access_scope: str = "public",
@@ -138,7 +135,6 @@ class ToolClient(BaseClient):
138
135
  """
139
136
  Retrieves a list of tools associated with the specified project.
140
137
 
141
- :param project_id: str - Unique identifier of the project.
142
138
  :param id: str - ID of the tool to filter by. Defaults to "" (no filtering).
143
139
  :param count: str - Number of tools to retrieve. Defaults to "100".
144
140
  :param access_scope: str - Access scope of the tools, either "public" or "private". Defaults to "public".
@@ -150,13 +146,13 @@ class ToolClient(BaseClient):
150
146
  endpoint = LIST_TOOLS_V2
151
147
  headers = {
152
148
  "Authorization": self.api_service.token,
153
- "ProjectId": project_id
149
+ "ProjectId": self.project_id
154
150
  }
155
151
 
156
152
  if scope and scope not in VALID_SCOPES:
157
153
  raise ValueError(f"Scope must be one of {', '.join(VALID_SCOPES)}.")
158
154
 
159
- logger.debug(f"Listing tools available for the project with ID: {project_id}")
155
+ logger.debug(f"Listing tools available for the project with ID: {self.project_id}")
160
156
 
161
157
  response = self.api_service.get(
162
158
  endpoint=endpoint,
@@ -173,14 +169,13 @@ class ToolClient(BaseClient):
173
169
  try:
174
170
  result = response.json()
175
171
  except JSONDecodeError as e:
176
- logger.error(f"Unable to list tools for project {project_id}: JSON parsing error (status {response.status_code}): {e}. Response: {response.text}")
177
- raise InvalidAPIResponseException(f"Unable to list tools for project {project_id}: {response.text}")
172
+ logger.error(f"Unable to list tools for project {self.project_id}: JSON parsing error (status {response.status_code}): {e}. Response: {response.text}")
173
+ raise InvalidAPIResponseException(f"Unable to list tools for project {self.project_id}: {response.text}")
178
174
 
179
175
  return result
180
176
 
181
177
  def get_tool(
182
178
  self,
183
- project_id: str,
184
179
  tool_id: str,
185
180
  revision: str = 0,
186
181
  version: int = 0,
@@ -189,7 +184,6 @@ class ToolClient(BaseClient):
189
184
  """
190
185
  Retrieves details of a specific tool from the specified project.
191
186
 
192
- :param project_id: str - Unique identifier of the project.
193
187
  :param tool_id: str - Unique identifier of the tool to retrieve.
194
188
  :param revision: str - Revision of the tool to retrieve. Defaults to 0 (latest revision).
195
189
  :param version: int - Version of the tool to retrieve. Defaults to 0 (latest version).
@@ -199,7 +193,7 @@ class ToolClient(BaseClient):
199
193
  endpoint = GET_TOOL_V2.format(toolId=tool_id)
200
194
  headers = {
201
195
  "Authorization": self.api_service.token,
202
- "ProjectId": project_id
196
+ "ProjectId": self.project_id
203
197
  }
204
198
 
205
199
  logger.debug(f"Retrieving detail of tool with ID: {tool_id}")
@@ -216,21 +210,19 @@ class ToolClient(BaseClient):
216
210
  try:
217
211
  result = response.json()
218
212
  except JSONDecodeError as e:
219
- logger.error(f"Unable to retrieve tool {tool_id} for project {project_id}: JSON parsing error (status {response.status_code}): {e}. Response: {response.text}")
220
- raise InvalidAPIResponseException(f"Unable to retrieve tool {tool_id} for project {project_id}: {response.text}")
213
+ logger.error(f"Unable to retrieve tool {tool_id} for project {self.project_id}: JSON parsing error (status {response.status_code}): {e}. Response: {response.text}")
214
+ raise InvalidAPIResponseException(f"Unable to retrieve tool {tool_id} for project {self.project_id}: {response.text}")
221
215
 
222
216
  return result
223
217
 
224
218
  def delete_tool(
225
219
  self,
226
- project_id: str,
227
220
  tool_id: str = None,
228
221
  tool_name: str = None
229
222
  ) -> dict | str:
230
223
  """
231
224
  Deletes a specific tool from the specified project.
232
225
 
233
- :param project_id: str - Unique identifier of the project.
234
226
  :param tool_id: str, optional - Unique identifier of the tool to delete. Defaults to None.
235
227
  :param tool_name: str, optional - Name of the tool to delete. Defaults to None.
236
228
  :return: dict or str - JSON response containing the result of the delete operation if successful,
@@ -243,7 +235,7 @@ class ToolClient(BaseClient):
243
235
  endpoint = DELETE_TOOL_V2.format(toolId=tool_id if tool_id else tool_name)
244
236
  headers = {
245
237
  "Authorization": self.api_service.token,
246
- "ProjectId": project_id
238
+ "ProjectId": self.project_id
247
239
  }
248
240
 
249
241
  if tool_id:
@@ -257,14 +249,13 @@ class ToolClient(BaseClient):
257
249
  )
258
250
 
259
251
  if response.status_code != 204:
260
- logger.error(f"Unable to delete tool {tool_id or tool_name} from project {project_id}: JSON parsing error (status {response.status_code}). Response: {response.text}")
261
- raise InvalidAPIResponseException(f"Unable to delete tool {tool_id or tool_name} from project {project_id}: {response.text}")
252
+ logger.error(f"Unable to delete tool {tool_id or tool_name} from project {self.project_id}: JSON parsing error (status {response.status_code}). Response: {response.text}")
253
+ raise InvalidAPIResponseException(f"Unable to delete tool {tool_id or tool_name} from project {self.project_id}: {response.text}")
262
254
  else:
263
255
  return {}
264
256
 
265
257
  def update_tool(
266
258
  self,
267
- project_id: str,
268
259
  tool_id: str,
269
260
  name: str = None,
270
261
  description: str = None,
@@ -282,7 +273,6 @@ class ToolClient(BaseClient):
282
273
  """
283
274
  Updates an existing tool in the specified project or upserts it if specified.
284
275
 
285
- :param project_id: str - Unique identifier of the project containing the tool.
286
276
  :param tool_id: str - Unique identifier of the tool to update. Required for update operations.
287
277
  :param name: str - Updated name of the tool. Must be non-empty, unique within the project, and exclude ':' or '/' if provided. Optional.
288
278
  :param description: str - Updated description of the tool's purpose, helping agents decide when to use it. Optional.
@@ -365,7 +355,7 @@ class ToolClient(BaseClient):
365
355
 
366
356
  headers = {
367
357
  "Authorization": self.api_service.token,
368
- "ProjectId": project_id
358
+ "ProjectId": self.project_id
369
359
  }
370
360
  response = self.api_service.put(
371
361
  endpoint=endpoint,
@@ -376,21 +366,19 @@ class ToolClient(BaseClient):
376
366
  try:
377
367
  result = response.json()
378
368
  except JSONDecodeError as e:
379
- logger.error(f"Unable to update tool {tool_id} in project {project_id}: JSON parsing error (status {response.status_code}): {e}. Response: {response.text}")
380
- raise InvalidAPIResponseException(f"Unable to update tool {tool_id} in project {project_id}: {response.text}")
369
+ logger.error(f"Unable to update tool {tool_id} in project {self.project_id}: JSON parsing error (status {response.status_code}): {e}. Response: {response.text}")
370
+ raise InvalidAPIResponseException(f"Unable to update tool {tool_id} in project {self.project_id}: {response.text}")
381
371
 
382
372
  return result
383
373
 
384
374
  def publish_tool_revision(
385
375
  self,
386
- project_id: str,
387
376
  tool_id: str,
388
377
  revision: str
389
378
  ):
390
379
  """
391
380
  Publishes a specific revision of a tool in the specified project.
392
381
 
393
- :param project_id: str - Unique identifier of the project.
394
382
  :param tool_id: str - Unique identifier of the tool to publish.
395
383
  :param revision: str - Revision of the tool to publish.
396
384
  :return: dict or str - JSON response containing the result of the publish operation if successful, otherwise the raw response text.
@@ -398,7 +386,7 @@ class ToolClient(BaseClient):
398
386
  endpoint = PUBLISH_TOOL_REVISION_V2.format(toolId=tool_id)
399
387
  headers = {
400
388
  "Authorization": self.api_service.token,
401
- "ProjectId": project_id
389
+ "ProjectId": self.project_id
402
390
  }
403
391
 
404
392
  logger.debug(f"Publishing revision {revision} for tool with ID {tool_id}")
@@ -413,14 +401,13 @@ class ToolClient(BaseClient):
413
401
  try:
414
402
  result = response.json()
415
403
  except JSONDecodeError as e:
416
- logger.error(f"Unable to publish revision {revision} for tool {tool_id} in project {project_id}: JSON parsing error (status {response.status_code}): {e}. Response: {response.text}")
417
- raise InvalidAPIResponseException(f"Unable to publish revision {revision} for tool {tool_id} in project {project_id}: {response.text}")
404
+ logger.error(f"Unable to publish revision {revision} for tool {tool_id} in project {self.project_id}: JSON parsing error (status {response.status_code}): {e}. Response: {response.text}")
405
+ raise InvalidAPIResponseException(f"Unable to publish revision {revision} for tool {tool_id} in project {self.project_id}: {response.text}")
418
406
 
419
407
  return result
420
408
 
421
409
  def get_parameter(
422
410
  self,
423
- project_id: str,
424
411
  tool_id: str = None,
425
412
  tool_public_name: str = None,
426
413
  revision: str = 0,
@@ -430,7 +417,6 @@ class ToolClient(BaseClient):
430
417
  """
431
418
  Retrieves details of parameters for a specific tool identified by either its ID or public name in the specified project.
432
419
 
433
- :param project_id: str - Unique identifier of the project.
434
420
  :param tool_id: str, optional - Unique identifier of the tool whose parameters are to be retrieved. Defaults to None.
435
421
  :param tool_public_name: str, optional - Public name of the tool whose parameters are to be retrieved. Defaults to None.
436
422
  :param revision: str - Revision of the parameters to retrieve. Defaults to "0" (latest revision).
@@ -450,7 +436,7 @@ class ToolClient(BaseClient):
450
436
  endpoint = GET_PARAMETER_V2.format(toolPublicName=tool_public_name) if tool_public_name else GET_PARAMETER_V2.format(toolPublicName=tool_id)
451
437
  headers = {
452
438
  "Authorization": self.api_service.token,
453
- "ProjectId": project_id
439
+ "ProjectId": self.project_id
454
440
  }
455
441
 
456
442
  response = self.api_service.get(
@@ -465,14 +451,13 @@ class ToolClient(BaseClient):
465
451
  try:
466
452
  result = response.json()
467
453
  except JSONDecodeError as e:
468
- logger.error(f"Unable to retrieve parameters for tool {tool_id or tool_public_name} in project {project_id}: JSON parsing error (status {response.status_code}): {e}. Response: {response.text}")
469
- raise InvalidAPIResponseException(f"Unable to retrieve parameters for tool {tool_id or tool_public_name} in project {project_id}: {response.text}")
454
+ logger.error(f"Unable to retrieve parameters for tool {tool_id or tool_public_name} in project {self.project_id}: JSON parsing error (status {response.status_code}): {e}. Response: {response.text}")
455
+ raise InvalidAPIResponseException(f"Unable to retrieve parameters for tool {tool_id or tool_public_name} in project {self.project_id}: {response.text}")
470
456
 
471
457
  return result
472
458
 
473
459
  def set_parameter(
474
460
  self,
475
- project_id: str,
476
461
  tool_id: str = None,
477
462
  tool_public_name: str = None,
478
463
  parameters: list = None
@@ -480,7 +465,6 @@ class ToolClient(BaseClient):
480
465
  """
481
466
  Sets or updates parameters for a specific tool identified by either its ID or public name in the specified project.
482
467
 
483
- :param project_id: str - Unique identifier of the project.
484
468
  :param tool_id: str, optional - Unique identifier of the tool whose parameters are to be set. Defaults to None.
485
469
  :param tool_public_name: str, optional - Public name of the tool whose parameters are to be set. Defaults to None.
486
470
  :param parameters: list - List of parameter dictionaries defining the tool's parameters.
@@ -497,7 +481,7 @@ class ToolClient(BaseClient):
497
481
  endpoint = SET_PARAMETER_V2.format(toolPublicName=tool_public_name) if tool_public_name else SET_PARAMETER_V2.format(toolPublicName=tool_id)
498
482
  headers = {
499
483
  "Authorization": self.api_service.token,
500
- "ProjectId": project_id,
484
+ "ProjectId": self.project_id,
501
485
  "Content-Type": "application/json",
502
486
  "Accept": "application/json"
503
487
  }
@@ -521,27 +505,23 @@ class ToolClient(BaseClient):
521
505
  try:
522
506
  result = response.json()
523
507
  except JSONDecodeError as e:
524
- logger.error(f"Unable to set parameters for tool {tool_id or tool_public_name} in project {project_id}: JSON parsing error (status {response.status_code}): {e}. Response: {response.text}")
525
- raise InvalidAPIResponseException(f"Unable to set parameters for tool {tool_id or tool_public_name} in project {project_id}: {response.text}")
508
+ logger.error(f"Unable to set parameters for tool {tool_id or tool_public_name} in project {self.project_id}: JSON parsing error (status {response.status_code}): {e}. Response: {response.text}")
509
+ raise InvalidAPIResponseException(f"Unable to set parameters for tool {tool_id or tool_public_name} in project {self.project_id}: {response.text}")
526
510
 
527
511
  return result
528
512
 
529
513
  def export_tool(
530
514
  self,
531
- project_id: str,
532
515
  tool_id: str,
533
516
  ) -> dict:
534
517
  """
535
518
  Retrieves details of a specific tool from the specified project.
536
519
 
537
- :param project_id: str - Unique identifier of the project containing the tool.
538
520
  :param tool_id: str - Unique identifier of the tool to retrieve.
539
521
  :return: dict - JSON response containing the tool details.
540
522
  :raises InvalidAPIResponseException: If the response cannot be parsed as JSON or an error occurs.
541
523
  :raises MissingRequirementException: If project_id or tool_id is not provided.
542
524
  """
543
- if not project_id:
544
- raise MissingRequirementException("Cannot retrieve tool without specifying project_id")
545
525
 
546
526
  if not tool_id:
547
527
  raise MissingRequirementException("tool_id must be specified in order to retrieve the tool")
@@ -549,7 +529,7 @@ class ToolClient(BaseClient):
549
529
  endpoint = EXPORT_TOOL_V2.format(toolId=tool_id)
550
530
  headers = {
551
531
  "Authorization": self.api_service.token,
552
- "ProjectId": project_id
532
+ "ProjectId": self.project_id
553
533
  }
554
534
 
555
535
  logger.debug(f"Exporting tool with ID {tool_id}")
@@ -561,7 +541,7 @@ class ToolClient(BaseClient):
561
541
  try:
562
542
  result = response.json()
563
543
  except JSONDecodeError as e:
564
- logger.error(f"Unable to export tool {tool_id} for project {project_id}: JSON parsing error (status {response.status_code}): {e}. Response: {response.text}")
565
- raise InvalidAPIResponseException(f"Unable to export tool {tool_id} for project {project_id}: {response.text}")
544
+ logger.error(f"Unable to export tool {tool_id} for project {self.project_id}: JSON parsing error (status {response.status_code}): {e}. Response: {response.text}")
545
+ raise InvalidAPIResponseException(f"Unable to export tool {tool_id} for project {self.project_id}: {response.text}")
566
546
 
567
547
  return result
@@ -37,8 +37,8 @@ class ToolMapper:
37
37
  """
38
38
  return [
39
39
  ToolMessage(
40
- description=msg["description"],
41
- type=msg["type"]
40
+ description=msg.get("description"),
41
+ type=msg.get("type")
42
42
  )
43
43
  for msg in messages_data
44
44
  ]
@@ -55,9 +55,9 @@ class ToolMapper:
55
55
  """
56
56
  tool_data = data.get("tool", data)
57
57
 
58
- name = tool_data["name"]
59
- description = tool_data["description"]
60
- scope = tool_data["scope"]
58
+ name = tool_data.get("name")
59
+ description = tool_data.get("description")
60
+ scope = tool_data.get("scope")
61
61
  parameter_data = tool_data.get("parameters")
62
62
  parameters = cls._map_parameters(parameter_data) if parameter_data else None
63
63
 
File without changes
@@ -0,0 +1,162 @@
1
+ from unittest import TestCase
2
+ from pygeai.chat.clients import ChatClient
3
+
4
+ chat_client: ChatClient
5
+
6
+ class TestChatGenerateImageIntegration(TestCase):
7
+
8
+ def setUp(self):
9
+ self.chat_client = ChatClient(alias="beta")
10
+ self.new_image = self.__load_image()
11
+
12
+
13
+ def __load_image(self):
14
+ return {
15
+ "model": "openai/gpt-image-1",
16
+ "prompt": "generate an image of a futuristic city skyline at sunset",
17
+ "n": 1,
18
+ "quality": "high",
19
+ "size": "1024x1536"
20
+ }
21
+
22
+
23
+ def __generate_image(self, image = None):
24
+ image = image if image is not None else self.new_image
25
+ return self.chat_client.generate_image(
26
+ model=image["model"],
27
+ prompt=image["prompt"],
28
+ n=image["n"],
29
+ quality=image["quality"],
30
+ size=image["size"],
31
+ aspect_ratio= image["aspect_ratio"] if "aspect_ratio" in image else None
32
+ )
33
+
34
+
35
+ def test_generate_image(self):
36
+ created_image = self.__generate_image()
37
+ self.assertEqual(len(created_image["data"]), 1, "Expected an image to be generated")
38
+
39
+
40
+ def test_generate_image_invalid_model(self):
41
+ self.new_image["model"] = "openai/gpt-image-10",
42
+ created_image = self.__generate_image()
43
+
44
+ self.assertEqual(
45
+ created_image["error"]["code"], 400,
46
+ "Expected a 400 code for invalid model"
47
+ )
48
+ self.assertEqual(
49
+ created_image["error"]["message"],
50
+ 'Provider \'["openai\' does not exists.',
51
+ "Expected an error message when model does not exists"
52
+ )
53
+
54
+
55
+ def test_generate_image_no_model(self):
56
+ self.new_image["model"] = ""
57
+ created_image = self.__generate_image()
58
+
59
+ self.assertEqual(
60
+ created_image["error"]["code"], 400,
61
+ "Expected a 400 code for no model"
62
+ )
63
+ self.assertEqual(
64
+ created_image["error"]["message"],
65
+ "Invalid 'model' name. Must follow pattern {provider}/{modelName}",
66
+ "Expected an error message when no model is provided"
67
+ )
68
+
69
+
70
+ def test_generate_image_no_prompt(self):
71
+ self.new_image["prompt"] = ""
72
+ created_image = self.__generate_image()
73
+
74
+ self.assertEqual(
75
+ created_image["error"]["type"],
76
+ "invalid_request_error",
77
+ "Expected a 400 code for no model"
78
+ )
79
+ self.assertEqual(
80
+ created_image["error"]["param"], "prompt",
81
+ "Expected an error message when no model is provided"
82
+ )
83
+
84
+
85
+ def test_generate_image_specific_n(self):
86
+ self.new_image["n"] = 2
87
+
88
+ created_image = self.__generate_image()
89
+ self.assertEqual(len(created_image["data"]), 2, "Expected two images to be generated")
90
+
91
+
92
+ def test_generate_image_no_n(self):
93
+ self.new_image["n"] = None # default is 1
94
+
95
+ created_image = self.__generate_image()
96
+ self.assertEqual(len(created_image["data"]), 1, "Expected an image to be generated")
97
+
98
+
99
+ def test_generate_image_no_supported_n(self):
100
+ self.new_image["model"] = "openai/dall-e-3"
101
+ self.new_image["n"] = 5
102
+
103
+ created_image = self.__generate_image()
104
+ self.assertIn(
105
+ "Invalid 'n': integer above maximum value",
106
+ created_image["error"]["message"],
107
+ "Expected an error message when n is not supported by the model"
108
+ )
109
+
110
+
111
+ def test_generate_image_no_quality(self):
112
+ self.new_image["quality"] = ""
113
+
114
+ created_image = self.__generate_image()
115
+ self.assertIn(
116
+ "Invalid value: ''. Supported values are: 'low', 'medium', 'high', and 'auto'",
117
+ created_image["error"]["message"],
118
+ "Expected an error message when quality is not provided"
119
+ )
120
+
121
+
122
+ def test_generate_image_no_supported_quality(self):
123
+ self.new_image["model"] = "openai/dall-e-3"
124
+
125
+ created_image = self.__generate_image()
126
+ self.assertIn(
127
+ "Invalid value: 'high'. Supported values are: 'standard' and 'hd'",
128
+ created_image["error"]["message"],
129
+ "Expected an error message when quality is not supported by the model"
130
+ )
131
+
132
+
133
+ def test_generate_image_no_size(self):
134
+ self.new_image["size"] = ""
135
+
136
+ created_image = self.__generate_image()
137
+ self.assertIn(
138
+ "Invalid value: ''. Supported values are: '1024x1024', '1024x1536', '1536x1024', and 'auto'",
139
+ created_image["error"]["message"],
140
+ "Expected an error message when no size is provided"
141
+ )
142
+
143
+
144
+ def test_generate_image_no_supported_size(self):
145
+ self.new_image["size"] = 1024
146
+ self.new_image["quality"] = None
147
+
148
+ created_image = self.__generate_image()
149
+ self.assertIn(
150
+ "Invalid type for 'size': expected one of '1024x1024', '1024x1536', '1536x1024', or 'auto', but got an integer instead",
151
+ created_image["error"]["message"],
152
+ "Expected an error message when no size is provided"
153
+ )
154
+
155
+
156
+ def test_generate_image_with_aspect_ratio(self):
157
+ self.new_image["model"] = "vertex_ai/imagen-3.0-generate-001"
158
+ self.new_image["aspect_ratio"] = "4:3"
159
+ self.new_image["quality"] = None
160
+
161
+ created_image = self.__generate_image()
162
+ self.assertEqual(len(created_image["data"]), 1, "Expected an image to be generated")