glaip-sdk 0.0.6a0__py3-none-any.whl → 0.0.8__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.
glaip_sdk/client/tools.py CHANGED
@@ -208,6 +208,44 @@ class ToolClient(BaseClient):
208
208
 
209
209
  return payload
210
210
 
211
+ def _handle_description_update(
212
+ self, update_data: dict[str, Any], description: str | None, current_tool: Tool
213
+ ) -> None:
214
+ """Handle description field in update payload."""
215
+ if description is not None:
216
+ update_data["description"] = description.strip()
217
+ elif hasattr(current_tool, "description") and current_tool.description:
218
+ update_data["description"] = current_tool.description
219
+
220
+ def _handle_tags_update(
221
+ self, update_data: dict[str, Any], kwargs: dict[str, Any], current_tool: Tool
222
+ ) -> None:
223
+ """Handle tags field in update payload."""
224
+ if kwargs.get("tags"):
225
+ if isinstance(kwargs["tags"], list):
226
+ update_data["tags"] = ",".join(
227
+ str(tag).strip() for tag in kwargs["tags"]
228
+ )
229
+ else:
230
+ update_data["tags"] = str(kwargs["tags"])
231
+ elif hasattr(current_tool, "tags") and current_tool.tags:
232
+ # Preserve existing tags if present
233
+ if isinstance(current_tool.tags, list):
234
+ update_data["tags"] = ",".join(
235
+ str(tag).strip() for tag in current_tool.tags
236
+ )
237
+ else:
238
+ update_data["tags"] = str(current_tool.tags)
239
+
240
+ def _handle_additional_kwargs(
241
+ self, update_data: dict[str, Any], kwargs: dict[str, Any]
242
+ ) -> None:
243
+ """Handle additional kwargs in update payload."""
244
+ excluded_keys = {"tags", "framework", "version"}
245
+ for key, value in kwargs.items():
246
+ if key not in excluded_keys:
247
+ update_data[key] = value
248
+
211
249
  def _build_update_payload(
212
250
  self,
213
251
  current_tool: Tool,
@@ -242,34 +280,14 @@ class ToolClient(BaseClient):
242
280
  ),
243
281
  }
244
282
 
245
- # Handle description with proper None handling
246
- if description is not None:
247
- update_data["description"] = description.strip()
248
- elif hasattr(current_tool, "description") and current_tool.description:
249
- update_data["description"] = current_tool.description
283
+ # Handle description update
284
+ self._handle_description_update(update_data, description, current_tool)
250
285
 
251
- # Handle tags - convert list to comma-separated string for API
252
- if kwargs.get("tags"):
253
- if isinstance(kwargs["tags"], list):
254
- update_data["tags"] = ",".join(
255
- str(tag).strip() for tag in kwargs["tags"]
256
- )
257
- else:
258
- update_data["tags"] = str(kwargs["tags"])
259
- elif hasattr(current_tool, "tags") and current_tool.tags:
260
- # Preserve existing tags if present
261
- if isinstance(current_tool.tags, list):
262
- update_data["tags"] = ",".join(
263
- str(tag).strip() for tag in current_tool.tags
264
- )
265
- else:
266
- update_data["tags"] = str(current_tool.tags)
286
+ # Handle tags update
287
+ self._handle_tags_update(update_data, kwargs, current_tool)
267
288
 
268
- # Add any other kwargs (excluding already handled ones)
269
- excluded_keys = {"tags", "framework", "version"}
270
- for key, value in kwargs.items():
271
- if key not in excluded_keys:
272
- update_data[key] = value
289
+ # Handle additional kwargs
290
+ self._handle_additional_kwargs(update_data, kwargs)
273
291
 
274
292
  return update_data
275
293
 
glaip_sdk/models.py CHANGED
@@ -119,6 +119,7 @@ class Tool(BaseModel):
119
119
  version: str | None = None
120
120
  tool_script: str | None = None
121
121
  tool_file: str | None = None
122
+ tags: str | list[str] | None = None
122
123
  _client: Any = None # Will hold client reference
123
124
 
124
125
  def _set_client(self, client: Any) -> "Tool":
@@ -246,6 +246,33 @@ def _handle_streaming_error(
246
246
  raise
247
247
 
248
248
 
249
+ def _process_sse_line(
250
+ line: str, buf: list[str], event_type: str | None, event_id: str | None
251
+ ) -> tuple[list[str], str | None, str | None, dict[str, Any] | None, bool]:
252
+ """Process a single SSE line and return updated state."""
253
+ result = _parse_sse_line(line, buf, event_type, event_id)
254
+ buf, event_type, event_id, event_data, completed = result
255
+ return buf, event_type, event_id, event_data, completed
256
+
257
+
258
+ def _yield_event_data(event_data: dict[str, Any] | None) -> Iterator[dict[str, Any]]:
259
+ """Yield event data if available."""
260
+ if event_data:
261
+ yield event_data
262
+
263
+
264
+ def _flush_remaining_buffer(
265
+ buf: list[str], event_type: str | None, event_id: str | None
266
+ ) -> Iterator[dict[str, Any]]:
267
+ """Flush any remaining data in buffer."""
268
+ if buf:
269
+ yield {
270
+ "event": event_type or "message",
271
+ "id": event_id,
272
+ "data": "\n".join(buf),
273
+ }
274
+
275
+
249
276
  def iter_sse_events(
250
277
  response: httpx.Response,
251
278
  timeout_seconds: float | None = None,
@@ -276,25 +303,16 @@ def iter_sse_events(
276
303
  if line is None:
277
304
  continue
278
305
 
279
- result = _parse_sse_line(line, buf, event_type, event_id)
280
- if len(result) == 5: # completion signal included
281
- buf, event_type, event_id, event_data, completed = result
282
- else: # normal case
283
- buf, event_type, event_id, event_data = result
284
- completed = False
306
+ buf, event_type, event_id, event_data, completed = _process_sse_line(
307
+ line, buf, event_type, event_id
308
+ )
285
309
 
286
- if event_data:
287
- yield event_data
310
+ yield from _yield_event_data(event_data)
288
311
  if completed:
289
312
  return
290
313
 
291
314
  # Flush any remaining data
292
- if buf:
293
- yield {
294
- "event": event_type or "message",
295
- "id": event_id,
296
- "data": "\n".join(buf),
297
- }
315
+ yield from _flush_remaining_buffer(buf, event_type, event_id)
298
316
 
299
317
  except Exception as e:
300
318
  _handle_streaming_error(e, timeout_seconds, agent_name)
@@ -329,11 +347,7 @@ async def aiter_sse_events(
329
347
  continue
330
348
 
331
349
  result = _parse_sse_line(line, buf, event_type, event_id)
332
- if len(result) == 5: # completion signal included
333
- buf, event_type, event_id, event_data, completed = result
334
- else: # normal case
335
- buf, event_type, event_id, event_data = result
336
- completed = False
350
+ buf, event_type, event_id, event_data, completed = result
337
351
 
338
352
  if event_data:
339
353
  yield event_data
@@ -352,6 +366,66 @@ async def aiter_sse_events(
352
366
  _handle_streaming_error(e, timeout_seconds, agent_name)
353
367
 
354
368
 
369
+ def _create_form_data(message: str) -> dict[str, Any]:
370
+ """Create form data with message and stream flag."""
371
+ return {"input": message, "message": message, "stream": True}
372
+
373
+
374
+ def _prepare_file_entry(
375
+ item: str | BinaryIO, stack: ExitStack
376
+ ) -> tuple[str, tuple[str, BinaryIO, str]]:
377
+ """Prepare a single file entry for multipart data."""
378
+ if isinstance(item, str):
379
+ return _prepare_path_entry(item, stack)
380
+ else:
381
+ return _prepare_stream_entry(item)
382
+
383
+
384
+ def _prepare_path_entry(
385
+ path_str: str, stack: ExitStack
386
+ ) -> tuple[str, tuple[str, BinaryIO, str]]:
387
+ """Prepare a file path entry."""
388
+ file_path = Path(path_str)
389
+ if not file_path.exists():
390
+ raise FileNotFoundError(f"File not found: {path_str}")
391
+
392
+ handle = stack.enter_context(open(file_path, "rb"))
393
+ return (
394
+ "files",
395
+ (
396
+ file_path.name,
397
+ handle,
398
+ "application/octet-stream",
399
+ ),
400
+ )
401
+
402
+
403
+ def _prepare_stream_entry(
404
+ file_obj: BinaryIO,
405
+ ) -> tuple[str, tuple[str, BinaryIO, str]]:
406
+ """Prepare a file object entry."""
407
+ if not hasattr(file_obj, "read"):
408
+ raise ValueError(f"Invalid file object: {file_obj}")
409
+
410
+ raw_name = getattr(file_obj, "name", "file")
411
+ filename = Path(raw_name).name if raw_name else "file"
412
+
413
+ try:
414
+ if hasattr(file_obj, "seek"):
415
+ file_obj.seek(0)
416
+ except (OSError, ValueError):
417
+ pass
418
+
419
+ return (
420
+ "files",
421
+ (
422
+ filename,
423
+ file_obj,
424
+ "application/octet-stream",
425
+ ),
426
+ )
427
+
428
+
355
429
  def prepare_multipart_data(message: str, files: list[str | BinaryIO]) -> MultipartData:
356
430
  """Prepare multipart form data for file uploads.
357
431
 
@@ -366,63 +440,13 @@ def prepare_multipart_data(message: str, files: list[str | BinaryIO]) -> Multipa
366
440
  FileNotFoundError: When a file path doesn't exist
367
441
  ValueError: When a file object is invalid
368
442
  """
369
-
370
- def _prepare_path_entry(
371
- path_str: str, stack: ExitStack
372
- ) -> tuple[str, tuple[str, BinaryIO, str]]:
373
- file_path = Path(path_str)
374
- if not file_path.exists():
375
- raise FileNotFoundError(f"File not found: {path_str}")
376
-
377
- handle = stack.enter_context(open(file_path, "rb"))
378
- return (
379
- "files",
380
- (
381
- file_path.name,
382
- handle,
383
- "application/octet-stream",
384
- ),
385
- )
386
-
387
- def _prepare_stream_entry(
388
- file_obj: BinaryIO,
389
- ) -> tuple[str, tuple[str, BinaryIO, str]]:
390
- if not hasattr(file_obj, "read"):
391
- raise ValueError(f"Invalid file object: {file_obj}")
392
-
393
- raw_name = getattr(file_obj, "name", "file")
394
- filename = Path(raw_name).name if raw_name else "file"
395
-
396
- try:
397
- if hasattr(file_obj, "seek"):
398
- file_obj.seek(0)
399
- except (OSError, ValueError):
400
- pass
401
-
402
- return (
403
- "files",
404
- (
405
- filename,
406
- file_obj,
407
- "application/octet-stream",
408
- ),
409
- )
410
-
411
- # Backend expects 'input' for the main prompt. Keep 'message' for
412
- # backward-compatibility with any legacy handlers.
413
- form_data = {"input": message, "message": message, "stream": True}
443
+ form_data = _create_form_data(message)
414
444
  stack = ExitStack()
415
445
  multipart_data = MultipartData(form_data, [])
416
446
  multipart_data._exit_stack = stack
417
447
 
418
448
  try:
419
- file_entries = []
420
- for item in files:
421
- if isinstance(item, str):
422
- file_entries.append(_prepare_path_entry(item, stack))
423
- else:
424
- file_entries.append(_prepare_stream_entry(item))
425
-
449
+ file_entries = [_prepare_file_entry(item, stack) for item in files]
426
450
  multipart_data.files = file_entries
427
451
  return multipart_data
428
452
  except Exception:
@@ -40,7 +40,9 @@ def extract_ids_from_export(items: list[Any]) -> list[str]:
40
40
  return ids
41
41
 
42
42
 
43
- def convert_export_to_import_format(data: dict[str, Any]) -> dict[str, Any]:
43
+ def convert_export_to_import_format(
44
+ data: dict[str, Any],
45
+ ) -> dict[str, Any]:
44
46
  """Convert export format to import-compatible format (extract IDs from objects).
45
47
 
46
48
  Args: