pygeai 0.4.0b2__py3-none-any.whl → 0.4.0b4__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.

Potentially problematic release.


This version of pygeai might be problematic. Click here for more details.

@@ -227,7 +227,7 @@ class SearchOptions(CustomBaseModel):
227
227
  "ingestion": self.ingestion.to_dict() if self.ingestion else None,
228
228
  "options": self.options,
229
229
  "rerank": self.rerank,
230
- "variables": self.variables.to_dict() if self.variables else None,
230
+ "variables": self.variables.to_list() if self.variables else None,
231
231
  "vectorStore": self.vector_store
232
232
  }
233
233
  return {k: v for k, v in result.items() if v is not None}
pygeai/lab/models.py CHANGED
@@ -94,7 +94,7 @@ class Model(CustomBaseModel):
94
94
  :param llm_config: Optional[LlmConfig] - Overrides default agent LLM settings.
95
95
  :param prompt: Optional[dict] - A tailored prompt specific to this model.
96
96
  """
97
- name: str = Field(..., alias="name")
97
+ name: Optional[str] = Field(None, alias="name")
98
98
  llm_config: Optional[LlmConfig] = Field(None, alias="llmConfig")
99
99
  prompt: Optional[Dict[str, Any]] = Field(None, alias="prompt")
100
100
 
@@ -161,12 +161,13 @@ class Prompt(CustomBaseModel):
161
161
  :param context: Optional[str] - Background context for the agent # NOT IMPLEMENTED YET
162
162
  :param examples: List[PromptExample] - List of example input-output pairs.
163
163
  """
164
- instructions: str = Field(..., alias="instructions")
164
+ instructions: Optional[str] = Field(None, alias="instructions")
165
165
  inputs: Optional[List[str]] = Field(None, alias="inputs")
166
166
  outputs: Optional[List[PromptOutput]] = Field([], alias="outputs")
167
167
  context: Optional[str] = Field(None, alias="context", description="Background context for the agent")
168
168
  examples: Optional[List[PromptExample]] = Field(None, alias="examples")
169
169
 
170
+ '''
170
171
  @field_validator("instructions")
171
172
  @classmethod
172
173
  def validate_instructions(cls, value: str) -> str:
@@ -174,6 +175,7 @@ class Prompt(CustomBaseModel):
174
175
  raise ValueError("instructions cannot be blank")
175
176
 
176
177
  return value
178
+ '''
177
179
 
178
180
  @field_validator("outputs", mode="before")
179
181
  @classmethod
@@ -687,7 +689,7 @@ class Tool(CustomBaseModel):
687
689
  :param status: Optional[str] - Current status of the tool (e.g., "active"), defaults to None.
688
690
  """
689
691
  name: str = Field(..., alias="name", description="The name of the tool")
690
- description: str = Field(..., alias="description", description="Description of the tool's purpose")
692
+ description: Optional[str] = Field(None, alias="description", description="Description of the tool's purpose")
691
693
  scope: str = Field("builtin", alias="scope", description="The scope of the tool (e.g., 'builtin', 'external', 'api')")
692
694
  parameters: Optional[List[ToolParameter]] = Field(None, alias="parameters", description="List of parameters required by the tool")
693
695
  access_scope: Optional[str] = Field(None, alias="accessScope", description="The access scope of the tool ('public' or 'private')")
@@ -730,6 +732,7 @@ class Tool(CustomBaseModel):
730
732
  raise ValueError("public_name is required if access_scope is 'public'")
731
733
  return self
732
734
 
735
+ '''
733
736
  @model_validator(mode="after")
734
737
  def validate_api_tool_requirements(self):
735
738
  if self.scope == "api" and not (self.open_api or self.open_api_json):
@@ -739,6 +742,7 @@ class Tool(CustomBaseModel):
739
742
  if len(param_keys) != len(set(param_keys)):
740
743
  raise ValueError("All parameter keys must be unique within the tool")
741
744
  return self
745
+ '''
742
746
 
743
747
  @field_validator("parameters", mode="before")
744
748
  @classmethod
@@ -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
  ]
@@ -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")
@@ -287,21 +287,18 @@ class TestAILabCreateAgentIntegration(TestCase):
287
287
  )
288
288
 
289
289
 
290
- @unittest.skip("Agent is getting created regardless of the prompt instructions being empty")
291
290
  def test_create_agent_no_prompt_instructions(self):
292
291
  self.new_agent.agent_data.prompt.instructions = ""
293
- with self.assertRaises(ValidationError) as exception:
294
- self.__create_agent()
295
- self.assertIn(
296
- "instructions",
297
- str(exception.exception),
298
- "Expected a validation error about allowed values for instructions"
299
- )
292
+ self.created_agent = self.__create_agent()
300
293
 
301
- self.assertIn(
302
- "Input should be a valid string [type=string_type, input_value=None, input_type=NoneType]",
303
- str(exception.exception),
304
- "Expected a validation error about allowed values for instructions"
294
+ self.assertTrue(
295
+ isinstance(self.created_agent, Agent),
296
+ "Expected a created agent"
297
+ )
298
+
299
+ self.assertIsNone(
300
+ self.created_agent.agent_data.prompt.instructions,
301
+ "Expected the created agent to not have prompt instructions"
305
302
  )
306
303
 
307
304
 
@@ -207,28 +207,20 @@ class TestAILabUpdateAgentIntegration(TestCase):
207
207
  f"Expected a validation error about allowed values for instructions when autopublish is {'enabled' if auto_publish else 'disabled'}"
208
208
  )
209
209
 
210
-
210
+ # TODO: Change validation when API behavior is fixed
211
211
  def test_update_agent_no_model(self):
212
212
  test_params = [ True, False ]
213
213
  self.agent_to_update.agent_data.models[0].name = ""
214
214
 
215
215
  for auto_publish in test_params:
216
216
  with self.subTest(input=auto_publish):
217
- if auto_publish == False:
218
- with self.assertRaises(ValidationError) as exception:
219
- self.__update_agent(automatic_publish=auto_publish)
217
+
218
+ # If the agent is not published, the API returns a warning message for invalid model name. However, the sdk mapping is not returning it.
219
+ if auto_publish == False:
220
+ updated_agent = self.__update_agent(automatic_publish=auto_publish)
220
221
  error_msg = str(exception.exception)
221
222
 
222
- self.assertIn(
223
- "name",
224
- error_msg,
225
- "Expected a validation error about empty model name"
226
- )
227
- self.assertIn(
228
- "Input should be a valid string [type=string_type, input_value=None, input_type=NoneType]",
229
- error_msg,
230
- "Expected a validation error about empty model name"
231
- )
223
+ self.assertTrue(self.isInstance(updated_agent), Agent)
232
224
  else:
233
225
  with self.assertRaises(APIError) as exception:
234
226
  self.__update_agent(automatic_publish=auto_publish)
File without changes
@@ -0,0 +1,293 @@
1
+ from unittest import TestCase
2
+ import unittest
3
+ import uuid
4
+ from pygeai.lab.managers import AILabManager
5
+ from pygeai.lab.models import Agent, AgentData, Prompt, LlmConfig, Model, Tool, ToolParameter,Sampling, PromptExample, PromptOutput
6
+ from pydantic import ValidationError
7
+ from pygeai.core.common.exceptions import APIError, InvalidAPIResponseException
8
+
9
+
10
+ class TestAILabCreateToolIntegration(TestCase):
11
+ def setUp(self):
12
+ """
13
+ Set up the test environment.
14
+ """
15
+ self.ai_lab_manager = AILabManager(alias="beta")
16
+ self.new_tool = self.__load_tool()
17
+ self.created_tool: Agent = None
18
+
19
+
20
+ def tearDown(self):
21
+ """
22
+ Clean up after each test if necessary.
23
+ This can be used to delete the created tool
24
+ """
25
+ if isinstance(self.created_tool, Tool):
26
+
27
+ self.ai_lab_manager.delete_tool(self.created_tool.id)
28
+
29
+
30
+ def __load_tool(self):
31
+ return Tool(
32
+ name=str(uuid.uuid4()),
33
+ description="Tool created for sdk testing purposes",
34
+ scope="builtin",
35
+ openApi="https://raw.usercontent.com//openapi.json",
36
+ openApiJson={"openapi": "3.0.0","info": {"title": "Simple API overview","version": "2.0.0"}},
37
+ accessScope="private",
38
+ reportEvents="None",
39
+ parameters=[{"key": "param", "description": "param description", "type":"app", "value":"param value", "data_type": "String", "isRequired": False}],
40
+ )
41
+
42
+
43
+ def __create_tool(self, tool=None, automatic_publish=False):
44
+ """
45
+ Helper to create a tool using ai_lab_manager.
46
+ """
47
+ return self.ai_lab_manager.create_tool(
48
+ tool=self.new_tool if tool is None else tool,
49
+ automatic_publish=automatic_publish
50
+ )
51
+
52
+
53
+ def test_create_tool_full_data(self):
54
+ self.created_tool = self.__create_tool()
55
+ created_tool = self.created_tool
56
+ tool = self.new_tool
57
+ self.assertTrue(isinstance(created_tool, Tool), "Expected a created tool")
58
+
59
+ # Assert the main fields of the created tool
60
+ self.assertIsNotNone(created_tool.id)
61
+ self.assertEqual(created_tool.name, tool.name)
62
+ self.assertEqual(created_tool.description, tool.description)
63
+ self.assertEqual(created_tool.scope, tool.scope)
64
+ self.assertEqual(created_tool.access_scope, tool.access_scope)
65
+ self.assertEqual(created_tool.open_api, tool.open_api)
66
+ self.assertEqual(created_tool.status, "active")
67
+
68
+ # Assert agentData fields
69
+ tool_param = created_tool.parameters[0]
70
+ self.assertTrue(isinstance(tool_param, ToolParameter), "Expected parameters to be of type ToolParameter")
71
+ self.assertEqual(tool_param.key, tool.parameters[0].key)
72
+ self.assertEqual(tool_param.data_type, tool.parameters[0].data_type)
73
+ self.assertEqual(tool_param.description, tool.parameters[0].description)
74
+ self.assertEqual(tool_param.is_required, tool.parameters[0].is_required)
75
+ self.assertEqual(tool_param.type, tool.parameters[0].type)
76
+ self.assertEqual(tool_param.value, tool.parameters[0].value)
77
+
78
+
79
+ def test_create_tool_minimum_required_data(self):
80
+ self.new_tool = Tool(
81
+ name=str(uuid.uuid4()),
82
+ description="Tool created for sdk testing purposes",
83
+ scope="builtin"
84
+ )
85
+ self.created_tool = self.__create_tool()
86
+ tool = self.new_tool
87
+
88
+ self.assertIsNotNone(self.created_tool.id)
89
+ self.assertEqual(self.created_tool.name, tool.name)
90
+ self.assertEqual(self.created_tool.description, tool.description)
91
+
92
+
93
+ def test_create_tool_without_required_data(self):
94
+ test_params = [ True, False ]
95
+
96
+ for auto_publish in test_params:
97
+
98
+ with self.subTest(input=auto_publish):
99
+ with self.assertRaises(ValidationError) as context:
100
+ self.new_tool = Tool(
101
+ name=str(uuid.uuid4())
102
+ )
103
+ self.__create_tool(automatic_publish=auto_publish)
104
+
105
+ self.assertIn("description", str(context.exception))
106
+ self.assertIn("Field required", str(context.exception))
107
+
108
+
109
+ def test_create_tool_no_name(self):
110
+ test_params = [ True, False ]
111
+
112
+ for auto_publish in test_params:
113
+ with self.subTest(input=auto_publish):
114
+ self.new_tool.name = ""
115
+ with self.assertRaises(APIError) as exception:
116
+ self.__create_tool(automatic_publish=auto_publish)
117
+
118
+ self.assertIn(
119
+ "Tool name cannot be empty.",
120
+ str(exception.exception),
121
+ f"Expected an error about the missing tool name with autopublish {'enabled' if auto_publish else 'disabled'}"
122
+ )
123
+
124
+
125
+ def test_create_tool_duplicated_name(self):
126
+ test_params = [ True, False ]
127
+
128
+ for auto_publish in test_params:
129
+
130
+ with self.subTest(input=auto_publish):
131
+ self.new_tool.name = "sdk_project_gemini_tool"
132
+ with self.assertRaises(APIError) as exception:
133
+ self.__create_tool(automatic_publish=auto_publish)
134
+ self.assertIn(
135
+ "Tool already exists [name=sdk_project_gemini_tool]..",
136
+ str(exception.exception),
137
+ f"Expected an error about duplicated tool name with autopublish {'enabled' if auto_publish else 'disabled'}"
138
+ )
139
+
140
+
141
+ def test_create_tool_invalid_name(self):
142
+ test_params = [ True, False ]
143
+
144
+ for auto_publish in test_params:
145
+ with self.subTest(input=auto_publish):
146
+ new_tool = self.__load_tool()
147
+ new_tool2 = self.__load_tool()
148
+
149
+ with self.assertRaises(APIError) as exception:
150
+ new_tool.name = f"{new_tool.name}:invalid"
151
+ self.__create_tool(tool=new_tool, automatic_publish=auto_publish)
152
+ self.assertIn(
153
+ "Invalid character in name (: is not allowed).",
154
+ str(exception.exception),
155
+ f"Expected an error about invalid character (:) in tool name with autopublish {'enabled' if auto_publish else 'disabled'}"
156
+ )
157
+
158
+ with self.assertRaises(APIError) as exception:
159
+ new_tool2.name = f"{new_tool2.name}/invalid"
160
+ self.__create_tool(tool=new_tool2, automatic_publish=auto_publish)
161
+ self.assertIn(
162
+ "Invalid character in name (/ is not allowed).",
163
+ str(exception.exception),
164
+ f"Expected an error about invalid character (/) in tool name with autopublish {'enabled' if auto_publish else 'disabled'}"
165
+ )
166
+
167
+
168
+ def test_create_tool_invalid_access_scope(self):
169
+ self.new_tool.access_scope = "project"
170
+ with self.assertRaises(ValueError) as exc:
171
+ self.__create_tool()
172
+ self.assertEqual(
173
+ str(exc.exception),
174
+ "Access scope must be one of public, private.",
175
+ "Expected a ValueError exception for invalid access scope"
176
+ )
177
+
178
+
179
+ def test_create_tool_default_scope(self):
180
+ self.new_tool.access_scope = None
181
+ self.created_tool = self.__create_tool()
182
+
183
+ self.assertEqual(self.created_tool.access_scope, "private", "Expected the default access scope to be 'private' when not specified")
184
+
185
+
186
+ def test_create_tool_no_public_name(self):
187
+ test_params = [ True, False ]
188
+
189
+ for auto_publish in test_params:
190
+
191
+ with self.subTest(input=auto_publish):
192
+ self.new_tool.access_scope = "public"
193
+ self.new_tool.public_name = None
194
+ with self.assertRaises(APIError) as exception:
195
+ self.__create_tool(automatic_publish=auto_publish)
196
+ self.assertIn(
197
+ "Tool publicName is required for tools with accessScope=public.",
198
+ str(exception.exception),
199
+ f"Expected an error about missing publicName for public access scope with autopublish {'enabled' if auto_publish else 'disabled'}"
200
+ )
201
+
202
+
203
+ def test_create_tool_invalid_public_name(self):
204
+ test_params = [ True, False ]
205
+
206
+ for auto_publish in test_params:
207
+ with self.subTest(input=auto_publish):
208
+ self.new_tool.access_scope = "public"
209
+ self.new_tool.public_name = "com.sdk.testing#" # Add invalid character to public name
210
+ with self.assertRaises(APIError) as exception:
211
+ self.__create_tool(automatic_publish=auto_publish)
212
+
213
+ self.assertIn(
214
+ "Invalid public name, it can only contain lowercase letters, numbers, periods (.), dashes (-), and underscores (_). Please remove any other characters.",
215
+ str(exception.exception),
216
+ f"The expected error about invalid publicName was not returned when autopublish is {'enabled' if auto_publish else 'disabled'}"
217
+ )
218
+
219
+
220
+ def test_create_tool_duplicated_public_name(self):
221
+ test_params = [ True, False ]
222
+
223
+ # Set the scope as public and assign a public name
224
+ self.new_tool.access_scope = "public"
225
+ self.new_tool.public_name=f"public_{self.new_tool.name}"
226
+ self.created_tool = self.__create_tool()
227
+
228
+ for auto_publish in test_params:
229
+ with self.subTest(input=auto_publish):
230
+
231
+ # Create a new with the same public name of created_tool
232
+ duplicated_pn_tool = self.__load_tool()
233
+ duplicated_pn_tool.access_scope = "public"
234
+ duplicated_pn_tool.public_name = self.created_tool.public_name
235
+
236
+ with self.assertRaises(APIError) as exception:
237
+ self.__create_tool(tool=duplicated_pn_tool, automatic_publish=auto_publish)
238
+ self.assertIn(
239
+ f"Tool already exists [publicName={self.created_tool.public_name}].",
240
+ str(exception.exception),
241
+ f"Expected an error about the duplicated public name when autopublish is {'enabled' if auto_publish else 'disabled'}"
242
+ )
243
+
244
+
245
+ def test_create_tool_api_scope_with_no_open_api(self):
246
+ self.new_tool.scope = "api"
247
+ with self.assertRaises(ValueError) as exception:
248
+ self.new_tool.openApi = ""
249
+ self.assertIn(
250
+ '"Tool" object has no field "openApi"',
251
+ str(exception.exception),
252
+ "Expected a validation error when openApi is not provided for api scope"
253
+ )
254
+
255
+
256
+ def test_create_tool_api_scope_with_no_open_api_json(self):
257
+ self.new_tool.scope = "api"
258
+ with self.assertRaises(ValueError) as exception:
259
+ self.new_tool.openApiJson = ""
260
+
261
+ self.assertIn(
262
+ '"Tool" object has no field "openApiJson"',
263
+ str(exception.exception),
264
+ "Expected a validation error when openApiJson is not provided for api scope"
265
+ )
266
+
267
+
268
+ def test_create_tool_invalid_scope(self):
269
+ test_params = [ True, False ]
270
+
271
+ for auto_publish in test_params:
272
+ with self.subTest(input=auto_publish):
273
+
274
+ with self.assertRaises(ValueError) as exception:
275
+ self.new_tool.scope = "source"
276
+ self.__create_tool()
277
+ self.assertIn(
278
+ 'Scope must be one of builtin, external, api, proxied',
279
+ str(exception.exception),
280
+ "Expected a validation error about allowed values for instructions"
281
+ )
282
+
283
+
284
+ def test_create_tool_autopublish(self):
285
+ self.created_tool = self.__create_tool(automatic_publish=True)
286
+ self.assertFalse(self.created_tool.is_draft, "Expected the tool to be published automatically")
287
+
288
+
289
+ def test_create_tool_autopublish_private_scope(self):
290
+ self.new_tool.access_scope = "private"
291
+
292
+ self.created_tool = self.__create_tool(automatic_publish=True)
293
+ self.assertFalse(self.created_tool.is_draft, "Expected the tool to be published automatically even with private scope")
@@ -0,0 +1,87 @@
1
+ from unittest import TestCase
2
+ import uuid
3
+ from pygeai.lab.managers import AILabManager
4
+ from pygeai.lab.models import Tool, AgentData, Prompt, LlmConfig, Model
5
+ from pygeai.core.common.exceptions import MissingRequirementException, InvalidAPIResponseException
6
+
7
+ ai_lab_manager: AILabManager
8
+
9
+ class TestAILabDeleteToolIntegration(TestCase):
10
+
11
+ def setUp(self):
12
+ self.ai_lab_manager = AILabManager(alias="beta")
13
+
14
+
15
+ def __create_tool(self):
16
+ """
17
+ Helper to create a tool
18
+ """
19
+ tool = Tool(
20
+ name=str(uuid.uuid4()),
21
+ description="Agent that translates from any language to english.",
22
+ scope="builtin"
23
+ )
24
+
25
+
26
+ return self.ai_lab_manager.create_tool(
27
+ tool=tool
28
+ )
29
+
30
+ def __delete_tool(self, tool_id: str = None, tool_name: str = None):
31
+ return self.ai_lab_manager.delete_tool(tool_id=tool_id, tool_name=tool_name)
32
+
33
+
34
+ def test_delete_tool_by_id(self):
35
+ created_tool = self.__create_tool()
36
+ deleted_tool = self.__delete_tool(tool_id=created_tool.id)
37
+
38
+ self.assertEqual(
39
+ deleted_tool.content,
40
+ "Tool deleted successfully",
41
+ "Expected confirmation message after deletion"
42
+ )
43
+
44
+
45
+ def test_delete_tool_by_name(self):
46
+ created_tool = self.__create_tool()
47
+ deleted_tool = self.__delete_tool(tool_name=created_tool.name)
48
+
49
+ self.assertEqual(
50
+ deleted_tool.content,
51
+ "Tool deleted successfully",
52
+ "Expected confirmation message after deletion"
53
+ )
54
+
55
+
56
+ def test_delete_tool_no_id_nor_name(self):
57
+ with self.assertRaises(MissingRequirementException) as exception:
58
+ self.__delete_tool()
59
+ self.assertIn(
60
+ "Either tool_id or tool_name must be provided",
61
+ str(exception.exception),
62
+ "Expected error message when neither tool_id nor tool_name is provided"
63
+ )
64
+
65
+
66
+ def test_delete_tool_invalid_id_valid_name(self):
67
+ invalid_id = "0026e53d-ea78-4cac-af9f-12650invalid"
68
+ created_tool = self.__create_tool()
69
+ with self.assertRaises(InvalidAPIResponseException) as exception:
70
+ self.__delete_tool(tool_name=created_tool.name, tool_id=invalid_id)
71
+
72
+ self.assertIn(
73
+ f"Tool not found [IdOrName= {invalid_id}].",
74
+ str(exception.exception),
75
+ "Expected error message for valid tool name and invalid tool id"
76
+ )
77
+
78
+
79
+ def test_delete_tool_invalid_name_valid_id(self):
80
+ created_tool = self.__create_tool()
81
+ deleted_tool = self.__delete_tool(tool_id=created_tool.id, tool_name="toolName")
82
+
83
+ self.assertEqual(
84
+ deleted_tool.content,
85
+ "Tool deleted successfully",
86
+ "Expected confirmation message after deletion"
87
+ )
@@ -0,0 +1,91 @@
1
+ from unittest import TestCase
2
+ import unittest
3
+ from pygeai.lab.managers import AILabManager
4
+ from pygeai.lab.models import Tool, FilterSettings
5
+ from pygeai.core.common.exceptions import APIError
6
+ import copy
7
+
8
+ ai_lab_manager: AILabManager
9
+
10
+ class TestAILabGetToolIntegration(TestCase):
11
+
12
+ def setUp(self):
13
+ self.ai_lab_manager = AILabManager(alias="beta")
14
+ self.tool_id = "e3e4d64f-ce52-467e-90a9-aa4d08425e82"
15
+ self.filter_settings = FilterSettings(
16
+ revision="0",
17
+ version="0"
18
+ )
19
+
20
+ def __get_tool(self, tool_id=None, filter_settings: FilterSettings = None):
21
+ return self.ai_lab_manager.get_tool(
22
+ tool_id=self.tool_id if tool_id is None else tool_id,
23
+ filter_settings=self.filter_settings if filter_settings is None else filter_settings
24
+ )
25
+
26
+ def test_get_tool(self):
27
+ tool = self.__get_tool()
28
+ self.assertIsInstance(tool, Tool, "Expected a tool")
29
+
30
+
31
+ @unittest.skip("Skipped: Validate that when no tool_id is provided, the complete tool list is returned")
32
+ def test_get_tool_no_tool_id(self):
33
+ with self.assertRaises(Exception) as context:
34
+ self.__get_tool(tool_id="")
35
+
36
+
37
+ def test_get_tool_invalid_tool_id(self):
38
+ invalid_id = "0026e53d-ea78-4cac-af9f-12650invalid"
39
+ with self.assertRaises(APIError) as context:
40
+ self.__get_tool(tool_id=invalid_id)
41
+ self.assertIn(
42
+ f"Tool not found [IdOrName= {invalid_id}].",
43
+ str(context.exception),
44
+ "Expected an error for invalid tool id"
45
+ )
46
+
47
+
48
+ def test_get_tool_no_revision(self):
49
+ filter_settings = copy.deepcopy(self.filter_settings)
50
+ filter_settings.revision = None
51
+ tool = self.__get_tool(filter_settings=filter_settings)
52
+
53
+ self.assertIsInstance(tool, Tool, "Expected a tool")
54
+ self.assertGreaterEqual(tool.revision, 1, "Expected tool revision to be the latest")
55
+
56
+
57
+ def test_get_tool_by_revision(self):
58
+ filter_settings = copy.deepcopy(self.filter_settings)
59
+ filter_settings.revision = "6"
60
+ tool = self.__get_tool(filter_settings=filter_settings)
61
+
62
+ self.assertEqual(6, tool.revision, "Expected agent revision to be 6")
63
+
64
+
65
+ def test_get_tool_by_earlier_revision(self):
66
+ filter_settings = copy.deepcopy(self.filter_settings)
67
+ filter_settings.revision = "2"
68
+ with self.assertRaises(APIError) as context:
69
+ self.__get_tool(filter_settings=filter_settings)
70
+ self.assertIn(
71
+ f"Requested revision not found [revision={filter_settings.revision}].",
72
+ str(context.exception),
73
+ "Expected an error for revision not found"
74
+ )
75
+
76
+ #TODO: The API is returning the version of the tool, but the sdk does not
77
+ def test_get_tool_no_version(self):
78
+ filter_settings = copy.deepcopy(self.filter_settings)
79
+ filter_settings.version = None
80
+ tool = self.__get_tool(filter_settings=filter_settings)
81
+
82
+ self.assertIsInstance(tool, Tool, "Expected a tool")
83
+
84
+
85
+ #TODO: The API is returning the version of the tool, but the sdk does not
86
+ def test_get_tool_by_version(self):
87
+ filter_settings = copy.deepcopy(self.filter_settings)
88
+ filter_settings.version = "1"
89
+ tool = self.__get_tool(filter_settings=filter_settings)
90
+
91
+ self.assertIsInstance(tool, Tool, "Expected a tool")
@@ -0,0 +1,38 @@
1
+ from unittest import TestCase
2
+ from pygeai.lab.managers import AILabManager
3
+ from pygeai.lab.models import ToolList, FilterSettings
4
+ import copy
5
+
6
+ ai_lab_manager: AILabManager
7
+
8
+ class TestAILabListToolsIntegration(TestCase):
9
+
10
+ def setUp(self):
11
+ self.ai_lab_manager = AILabManager(alias="beta")
12
+ self.filter_settings = FilterSettings(
13
+ allow_external=False,
14
+ allow_drafts=True,
15
+ access_scope="private"
16
+ )
17
+ """ list-tools or lt List tools
18
+ --project-id or --pid ID of the project
19
+ --id ID of the tool to filter by. Defaults to an empty string (no filtering).
20
+ --count Number of tools to retrieve. Defaults to '100'.
21
+ --access-scope Access scope of the tools, either "public" or "private". Defaults to "public".
22
+ --allow-drafts Whether to include draft tools. Defaults to 1 (True).
23
+ --scope Scope of the tools, must be 'builtin', 'external', or 'api'. Defaults to 'api'.
24
+ --allow-external Whether to include external tools. Defaults to 1 (True). """
25
+
26
+ def __list_tools(self, filter_settings: FilterSettings = None):
27
+ filter_settings = filter_settings if filter_settings != None else self.filter_settings
28
+ return self.ai_lab_manager.list_tools(filter_settings=filter_settings)
29
+
30
+ def test_private_list_tools(self):
31
+ result = self.__list_tools()
32
+ self.assertIsInstance(result, ToolList , "Expected a list of tools")
33
+ print(result)
34
+ for tool in result.tools:
35
+ self.assertTrue(tool.access_scope == "private", "Expected all tools to be private")
36
+
37
+
38
+
@@ -0,0 +1,48 @@
1
+ from pygeai.lab.managers import AILabManager
2
+ from pygeai.lab.models import Agent, AgentData, Prompt, LlmConfig, Model, Sampling, PromptExample, PromptOutput
3
+
4
+ agent = Agent(
5
+ id="f64ba214-152b-4dd4-be0d-2920da415f5d",
6
+ status="active",
7
+ name="Private Translator V25",
8
+ access_scope="private",
9
+ public_name="com.genexus.geai.private_translator#",
10
+ job_description="Translates",
11
+ avatar_image="https://www.shareicon.net/data/128x128/2016/11/09/851442_logo_512x512.png",
12
+ description="Agent that translates from any language to english.",
13
+ is_draft=False,
14
+ is_readonly=False,
15
+ revision=1,
16
+ version=None,
17
+ agent_data=AgentData(
18
+ prompt=Prompt(
19
+ instructions="the user will provide a text, you must return the same text translated to english",
20
+ inputs=["text", "avoid slang indicator"],
21
+ outputs=[
22
+ PromptOutput(key="translated_text", description="translated text, with slang or not depending on the indication. in plain text."),
23
+ PromptOutput(key="summary", description="a summary in the original language of the text to be translated, also in plain text.")
24
+ ],
25
+ examples=[
26
+ PromptExample(input_data="opitiiiis mundo [no-slang]", output='{"translated_text":"hello world","summary":"saludo"}'),
27
+ PromptExample(input_data="esto es una prueba pincheguey [keep-slang]", output='{"translated_text":"this is a test pal","summary":"prueba"}')
28
+ ]
29
+ ),
30
+ llm_config=LlmConfig(
31
+ max_tokens=5000,
32
+ timeout=0,
33
+ sampling=Sampling(temperature=0.5, top_k=0, top_p=0)
34
+ ),
35
+ models=[Model(name="gpt-4-turbo-preview")]
36
+ )
37
+ )
38
+
39
+
40
+ manager = AILabManager()
41
+ result = manager.create_agent(
42
+ agent=agent,
43
+ automatic_publish=False
44
+ )
45
+
46
+
47
+ print(f"Agent: {agent.to_dict()}")
48
+
@@ -0,0 +1,48 @@
1
+ from pygeai.lab.managers import AILabManager
2
+ from pygeai.lab.models import Agent, AgentData, Prompt, LlmConfig, Model, Sampling, PromptExample, PromptOutput
3
+
4
+ agent = Agent(
5
+ id="f64ba214-152b-4dd4-be0d-2920da415f5d",
6
+ status="active",
7
+ name="Private Translator V25",
8
+ access_scope="private",
9
+ public_name="com.genexus.geai.private_translator_25",
10
+ job_description="Translates",
11
+ avatar_image="https://www.shareicon.net/data/128x128/2016/11/09/851442_logo_512x512.png",
12
+ description="Agent that translates from any language to english.",
13
+ is_draft=False,
14
+ is_readonly=False,
15
+ revision=1,
16
+ version=None,
17
+ agent_data=AgentData(
18
+ prompt=Prompt(
19
+ instructions="the user will provide a text, you must return the same text translated to english",
20
+ inputs=["text", "avoid slang indicator"],
21
+ outputs=[
22
+ PromptOutput(key="translated_text", description="translated text, with slang or not depending on the indication. in plain text."),
23
+ PromptOutput(key="summary", description="a summary in the original language of the text to be translated, also in plain text.")
24
+ ],
25
+ examples=[
26
+ PromptExample(input_data="opitiiiis mundo [no-slang]", output='{"translated_text":"hello world","summary":"saludo"}'),
27
+ PromptExample(input_data="esto es una prueba pincheguey [keep-slang]", output='{"translated_text":"this is a test pal","summary":"prueba"}')
28
+ ]
29
+ ),
30
+ llm_config=LlmConfig(
31
+ max_tokens=5000,
32
+ timeout=0,
33
+ sampling=Sampling(temperature=0.5, top_k=0, top_p=0)
34
+ ),
35
+ models=[Model(name="gpt-4-turbo-preview")]
36
+ )
37
+ )
38
+
39
+
40
+ manager = AILabManager()
41
+ result = manager.create_agent(
42
+ agent=agent,
43
+ automatic_publish=False
44
+ )
45
+
46
+
47
+ print(f"Agent: {agent.to_dict()}")
48
+
@@ -0,0 +1,50 @@
1
+ from pygeai.lab.managers import AILabManager
2
+ from pygeai.lab.models import Tool, ToolParameter
3
+
4
+ parameters = [
5
+ ToolParameter(
6
+ key="input",
7
+ data_type="String",
8
+ description="some input that the tool needs.",
9
+ is_required=True
10
+ ),
11
+ ToolParameter(
12
+ key="some_nonsensitive_id",
13
+ data_type="String",
14
+ description="Configuration that is static, in the sense that whenever the tool is used, the value for this parameter is configured here. The llm will not know about it.",
15
+ is_required=True,
16
+ type="config",
17
+ from_secret=False,
18
+ value="b001e30b4016001f5f76b9ae9215ac40"
19
+ ),
20
+ ToolParameter(
21
+ key="api_token",
22
+ data_type="String",
23
+ description="Configuration that is static, but it is sensitive information . The value is stored in secret-manager",
24
+ is_required=True,
25
+ type="config",
26
+ value="0cd84dc7-f3f5-4a03-9288-cdfd8d72fde1"
27
+ )
28
+ ]
29
+
30
+ tool = Tool(
31
+ name="sample_tool_v5",
32
+ access_scope="private",
33
+ public_name="sample.tool.test#",
34
+ description="a builtin tool that does something but really does nothing cos it does not exist.",
35
+ scope="builtin",
36
+ parameters=parameters
37
+ )
38
+
39
+
40
+ manager = AILabManager()
41
+
42
+
43
+ result = manager.create_tool(
44
+ tool=tool,
45
+ automatic_publish=False
46
+ )
47
+
48
+ print(f"Created tool: {result.name}, ID: {result.id}")
49
+ print(f"Description: {result.description}")
50
+ print(f"Messages: {result.messages}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pygeai
3
- Version: 0.4.0b2
3
+ Version: 0.4.0b4
4
4
  Summary: Software Development Kit to interact with Globant Enterprise AI.
5
5
  Author-email: Globant <geai-sdk@globant.com>
6
6
  License-Expression: MIT
@@ -41,11 +41,11 @@ Dynamic: license-file
41
41
 
42
42
  PyGEAI is a Software Development Kit (SDK) for interacting with [Globant Enterprise AI](https://wiki.genexus.com/enterprise-ai/wiki?8,Table+of+contents%3AEnterprise+AI). It comprises libraries, tools, code samples, and documentation to simplify your experience with the platform.
43
43
 
44
- ## Repository
44
+ ## Terms and conditions
45
45
 
46
- Find the PyGEAI source code and documentation in the following GitHub repository:
46
+ By using the Python SDK to interact with Globant Enterprise AI, you agree with the following Terms and Conditions:
47
47
 
48
- [GitHub repository](https://github.com/RT-GEN029-GI/pygeai)
48
+ [Terms and Conditions](https://www.globant.com/enterprise-ai/terms-of-use)
49
49
 
50
50
  ## Compatibility
51
51
  This package is compatible with the Globant Enterprise AI release from June 2025.
@@ -16,7 +16,7 @@ pygeai/assistant/rag/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hS
16
16
  pygeai/assistant/rag/clients.py,sha256=9a8loVLRnVkA3nHvvOpbdUEhy_TEnHm1rhdBYrBVABE,15893
17
17
  pygeai/assistant/rag/endpoints.py,sha256=7YlHIvAYj3-xsCWtVMDYobxXbAO0lCo9yJdOrQxwCrQ,1145
18
18
  pygeai/assistant/rag/mappers.py,sha256=n3aeNXqz_7zq_JWq5wJfeNX1kvV3arOxAoUsqRYOZsc,8645
19
- pygeai/assistant/rag/models.py,sha256=xqYJiFMpDztRdjsb-pSAd1k3POmLJFM4hH6SaJ_4pto,15732
19
+ pygeai/assistant/rag/models.py,sha256=g5UiHuRjobgU1WgUMxeBzXykxgJ5q7eb_YY8qDciNvw,15732
20
20
  pygeai/assistant/rag/responses.py,sha256=fY97ibsCVLQ3Ssnjuvj-JeA883WqjOw7ZdxbpQp_B1E,196
21
21
  pygeai/chat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
22
  pygeai/chat/clients.py,sha256=QEyeTIPxp6xXKAEkE_XkjIxZDnaH808GKhIYr7ulrSA,10785
@@ -144,7 +144,7 @@ pygeai/health/endpoints.py,sha256=UAzMcqSXZtMj4r8M8B7a_a5LT6X_jMFNsCTvcsjNTYA,71
144
144
  pygeai/lab/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
145
145
  pygeai/lab/constants.py,sha256=ddgDnXP4GD0woi-FUJaJXzaWS3H6zmDN0B-v8utM95Q,170
146
146
  pygeai/lab/managers.py,sha256=9wV6SipzsIwFP9SXsKqZ0X5x6KbUuo6iCxPZF4zNGj4,72714
147
- pygeai/lab/models.py,sha256=uxhmogA0234eXXmqsVRE7EQQsmqx_BBHYXjhWkKso40,71386
147
+ pygeai/lab/models.py,sha256=1m41gSqpXZVO9AcPVxzlsC-TgxZcCsgGUbpN5zoDMjU,71451
148
148
  pygeai/lab/runners.py,sha256=-uaCPHpFyiKtVOxlEjPjAc9h-onSdGAcYJ5IAZPqlb0,4147
149
149
  pygeai/lab/agents/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
150
150
  pygeai/lab/agents/clients.py,sha256=2SYNjyD9LZaOUxmTcrwzqcHwW7K1wAmYwen_u0G-RfU,22659
@@ -153,7 +153,7 @@ pygeai/lab/agents/mappers.py,sha256=K6rxsO2Nq6GglmCUmyDKUNmzTG8HRbCelap6qaVKXQw,
153
153
  pygeai/lab/processes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
154
154
  pygeai/lab/processes/clients.py,sha256=C1YYJE7c5qJSZUIDKzo5kK-LOZP6jbdrG01aE8zoEYg,51403
155
155
  pygeai/lab/processes/endpoints.py,sha256=nFIEcNP22xe4j6URI6KcwTh7h-xgYjYYuHT6PDPiO3I,2100
156
- pygeai/lab/processes/mappers.py,sha256=QBpDT3wlyRNNUMa_vDP2vhQ4pD0vwnjXc1ahckfRVX0,15807
156
+ pygeai/lab/processes/mappers.py,sha256=YOWcVKdcJmLMAq-f3qevzqQ8L_hjb0_jVXBdCHutpzk,15815
157
157
  pygeai/lab/spec/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
158
158
  pygeai/lab/spec/loader.py,sha256=Dq9MhLqFwF4RPdBBaqKPGqt43-PrNlsHpe-NXe4S0qQ,709
159
159
  pygeai/lab/spec/parsers.py,sha256=oG7tY-GylweRxpvtCl3p53t0IoTX3UZFiB77x__3Qp8,646
@@ -164,7 +164,7 @@ pygeai/lab/strategies/mappers.py,sha256=6C_jubAVXMKLGQy5NUD0OX7SlrU2mLe2QsgzeJ1-
164
164
  pygeai/lab/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
165
165
  pygeai/lab/tools/clients.py,sha256=TfgociWyvzao3B6g2VfjAb6FmXp1YqmJRG2TT7RwOic,27441
166
166
  pygeai/lab/tools/endpoints.py,sha256=HiGoMs4OVeCgH7EAERTtifFPl53NryA1Awh7D6AO8bA,699
167
- pygeai/lab/tools/mappers.py,sha256=6xcR7lIOYrvUG8SERQIx_bsK-_To316BUql8UPLHWco,4978
167
+ pygeai/lab/tools/mappers.py,sha256=bYi5k36h0k4mCvOnV-r8YOHKz0U9P0mH21GNs20w2eM,4998
168
168
  pygeai/man/__init__.py,sha256=gqGI92vUPt6RPweoWX3mTUYPWNDlm6aGUjQOnYXqthk,53
169
169
  pygeai/man/man1/__init__.py,sha256=CFvES6cP_sbhgpm-I-QSbPC1f7Bw7cFsMW2-sxm4FtM,54
170
170
  pygeai/man/man1/geai-proxy.1,sha256=N5jtjzS5dB3JjAkG0Rw8EBzhC6Jgoy6zbS7XDgcE4EA,6735
@@ -268,15 +268,22 @@ pygeai/tests/gam/test_clients.py,sha256=vNz-4ux0cubztTY-_fEPWEoMCt5VAmZLecd0V-sE
268
268
  pygeai/tests/health/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
269
269
  pygeai/tests/health/test_clients.py,sha256=kfakkZHFMfo2IAN-PzmtMGmgR4iNiN1RpRopI--0qHI,1525
270
270
  pygeai/tests/integration/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
271
+ pygeai/tests/integration/chat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
272
+ pygeai/tests/integration/chat/test_generate_image.py,sha256=byCQQK6dIy68yPAhAa66bh7N0Xz5WnKSClx1vaIIzZA,5431
271
273
  pygeai/tests/integration/lab/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
272
274
  pygeai/tests/integration/lab/agents/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
273
275
  pygeai/tests/integration/lab/agents/test_agents_list.py,sha256=F2KUCdeiaBC3dn8ARNWqSz_kJcRyA0HC1nquhamN35Q,4187
274
- pygeai/tests/integration/lab/agents/test_create_agent.py,sha256=J3_QYCQM02LVdYZN3Y4nFmqn43_BgbV6ZELtLhZnjl0,14475
276
+ pygeai/tests/integration/lab/agents/test_create_agent.py,sha256=yL3owPJP0RoHSy3lEAdH06LJXqxgt3f2l2plk75d4cc,14206
275
277
  pygeai/tests/integration/lab/agents/test_create_sharing_link.py,sha256=y-e8Q_TfuLz7XXMRERSKA_-OQJUMBIsJcK0lQ0Oh858,2467
276
278
  pygeai/tests/integration/lab/agents/test_delete_agent.py,sha256=sb3RfoZJdzQvcVdNcXY2C2FO3yY1ZNiAZ_6Ay6f331E,2524
277
279
  pygeai/tests/integration/lab/agents/test_get_agent.py,sha256=oW1F6SENvhL9jZC021Rj-f_Xek2DSTx3SsZBr3YT6Hk,3666
278
280
  pygeai/tests/integration/lab/agents/test_publish_agent_revision.py,sha256=4vpuAVBenLCyWvaKTA2PQVLn_e-abGDscngUzZm3dMs,5284
279
- pygeai/tests/integration/lab/agents/test_update_agent.py,sha256=vme9TJij46U55oOvUYGdKRyzZn-UKzXXYGJH0-zCeRs,11959
281
+ pygeai/tests/integration/lab/agents/test_update_agent.py,sha256=1seho_GOtOHik_YJ9GPA4McSZorohhFRvuzY7UYXbxo,11726
282
+ pygeai/tests/integration/lab/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
283
+ pygeai/tests/integration/lab/tools/test_create_tool.py,sha256=G74P0_DUMpfCH3HBvImpUKD_aMHT8V6uPyVzo2V81lg,12479
284
+ pygeai/tests/integration/lab/tools/test_delete_tool.py,sha256=wy979nZh8ERd-k3jhJTjHqG4wxWE4sx-r4yn2nBc7Aw,2913
285
+ pygeai/tests/integration/lab/tools/test_get_tool.py,sha256=3fVDQlklmvOUgYDp0ATv5RqRmApgD4Qw_YGqjBOaOOo,3437
286
+ pygeai/tests/integration/lab/tools/test_list_tools.py,sha256=afJ-Y11uCJEvfzs0FzdjExqksO3PswglWHEg2_nBJ-4,1725
280
287
  pygeai/tests/lab/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
281
288
  pygeai/tests/lab/test_managers.py,sha256=AsOAvyCkRpbskEy214aV2TwrqilWH6bxOiTWDOb1twQ,29778
282
289
  pygeai/tests/lab/test_mappers.py,sha256=2cLSggf168XWFpeZeBR7uJ-8C32TKb7qA91i_9fr_b0,11409
@@ -368,6 +375,8 @@ pygeai/tests/snippets/lab/runner_1.py,sha256=QD92MvC22wpWj6YyrSgpp46EcL0ciac2x1z
368
375
  pygeai/tests/snippets/lab/agents/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
369
376
  pygeai/tests/snippets/lab/agents/create_agent.py,sha256=EVyfQzDST9A9KaM0ToZJKlJ7yCbfhpSVkVK_MaUVQPw,1875
370
377
  pygeai/tests/snippets/lab/agents/create_agent_2.py,sha256=jd7HKhle_c0S0vI80AejOyLaNqBWkILlRF_znzyCGcQ,1879
378
+ pygeai/tests/snippets/lab/agents/create_agent_edge_case.py,sha256=8dA9giNdsHjFZsIWTlBFk8e1QS3YtbZxklsVu0ZrDrk,1877
379
+ pygeai/tests/snippets/lab/agents/create_agent_without_instructions.py,sha256=jd7HKhle_c0S0vI80AejOyLaNqBWkILlRF_znzyCGcQ,1879
371
380
  pygeai/tests/snippets/lab/agents/delete_agent.py,sha256=GfDX667_V3tZMz3vjsbrxoFZggzpwjZYH_PVO2Qjw5s,269
372
381
  pygeai/tests/snippets/lab/agents/get_agent.py,sha256=bcqloJHwmNsFjEfri6QIRaTuHzwLtfEqIQPIC5pdkWQ,516
373
382
  pygeai/tests/snippets/lab/agents/get_sharing_link.py,sha256=2mYPwMgFFbqzmWZEBrFYSjdZscVyjJYCs4A3L0x8Sr8,355
@@ -394,6 +403,7 @@ pygeai/tests/snippets/lab/strategies/list_reasoning_strategies.py,sha256=4pqsW16
394
403
  pygeai/tests/snippets/lab/strategies/update_reasoning_strategy.py,sha256=OIoHNkdnXbC9GacPgXUG1jKlVizVtWfRI-E8_3xF5b0,889
395
404
  pygeai/tests/snippets/lab/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
396
405
  pygeai/tests/snippets/lab/tools/create_tool.py,sha256=dreldR8QIu9Q9tjOG2hkD6b8KZa_VrwSfSnCrzPgQLA,1400
406
+ pygeai/tests/snippets/lab/tools/create_tool_edge_case.py,sha256=8w4mvoRTMTyc70yYm2bgV2dr_Rh5QpPJR8VoqX-eY-s,1465
397
407
  pygeai/tests/snippets/lab/tools/delete_tool.py,sha256=pnIkYvdP7X7Gx79AMK5MSVliIXdHSpyVwRhH3kgi7ys,452
398
408
  pygeai/tests/snippets/lab/tools/get_parameter.py,sha256=dXdlHhoWxzZIYdsvHKnLLT5Vff2Tip46XCoOo-B8Gf0,490
399
409
  pygeai/tests/snippets/lab/tools/get_tool.py,sha256=-fkKAE6nflwtLY_Lf6itZJyx_9aanFp-TSDHUzub1AM,477
@@ -482,9 +492,9 @@ pygeai/vendor/a2a/utils/helpers.py,sha256=6Tbd8SVfXvdNEk6WYmLOjrAxkzFf1aIg8dkFfB
482
492
  pygeai/vendor/a2a/utils/message.py,sha256=gc_EKO69CJ4HkR76IFgsy-kENJz1dn7CfSgWJWvt-gs,2197
483
493
  pygeai/vendor/a2a/utils/task.py,sha256=BYRA_L1HpoUGJAVlyHML0lCM9Awhf2Ovjj7oPFXKbh0,1647
484
494
  pygeai/vendor/a2a/utils/telemetry.py,sha256=VvSp1Ztqaobkmq9-3sNhhPEilJS32-JTSfKzegkj6FU,10861
485
- pygeai-0.4.0b2.dist-info/licenses/LICENSE,sha256=eHfqo7-AWS8cMq0cg03lq7owsLeCmZA-xS5L0kuHnl8,1474
486
- pygeai-0.4.0b2.dist-info/METADATA,sha256=bpntcqljAoYNvhEDrvlR5sjc3QDZtp2MxaFPmtCnepA,6882
487
- pygeai-0.4.0b2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
488
- pygeai-0.4.0b2.dist-info/entry_points.txt,sha256=OAmwuXVCQBTCE3HeVegVd37hbhCcp9TPahvdrCuMYWw,178
489
- pygeai-0.4.0b2.dist-info/top_level.txt,sha256=bJFwp2tURmCfB94yXDF7ylvdSJXFDDJsyUOb-7PJgwc,7
490
- pygeai-0.4.0b2.dist-info/RECORD,,
495
+ pygeai-0.4.0b4.dist-info/licenses/LICENSE,sha256=eHfqo7-AWS8cMq0cg03lq7owsLeCmZA-xS5L0kuHnl8,1474
496
+ pygeai-0.4.0b4.dist-info/METADATA,sha256=plyocQ_kqcryzOcFS26Ontgdg5bv5yMrbcitm3bsOcQ,6940
497
+ pygeai-0.4.0b4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
498
+ pygeai-0.4.0b4.dist-info/entry_points.txt,sha256=OAmwuXVCQBTCE3HeVegVd37hbhCcp9TPahvdrCuMYWw,178
499
+ pygeai-0.4.0b4.dist-info/top_level.txt,sha256=bJFwp2tURmCfB94yXDF7ylvdSJXFDDJsyUOb-7PJgwc,7
500
+ pygeai-0.4.0b4.dist-info/RECORD,,