glaip-sdk 0.0.3__py3-none-any.whl → 0.0.4__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/cli/utils.py CHANGED
@@ -198,16 +198,6 @@ def get_client(ctx) -> Client:
198
198
  )
199
199
 
200
200
 
201
- # ----------------------------- Small helpers ----------------------------- #
202
-
203
-
204
- def safe_getattr(obj: Any, attr: str, default: Any = None) -> Any:
205
- try:
206
- return getattr(obj, attr)
207
- except Exception:
208
- return default
209
-
210
-
211
201
  # ----------------------------- Secret masking ---------------------------- #
212
202
 
213
203
  _DEFAULT_MASK_FIELDS = {
@@ -495,12 +485,12 @@ def output_result(
495
485
  return
496
486
 
497
487
  if success_message:
498
- console.print(f"[green]✅ {success_message}[/green]")
488
+ console.print(Text(f"[green]✅ {success_message}[/green]"))
499
489
 
500
490
  if panel_title:
501
491
  console.print(Panel(Pretty(data), title=panel_title, border_style="blue"))
502
492
  else:
503
- console.print(f"[cyan]{title}:[/cyan]")
493
+ console.print(Text(f"[cyan]{title}:[/cyan]"))
504
494
  console.print(Pretty(data))
505
495
 
506
496
 
@@ -576,7 +566,7 @@ def output_list(
576
566
  return
577
567
 
578
568
  if not items:
579
- console.print(f"[yellow]No {title.lower()} found.[/yellow]")
569
+ console.print(Text(f"[yellow]No {title.lower()} found.[/yellow]"))
580
570
  return
581
571
 
582
572
  # Sort by name by default (unless disabled)
@@ -737,8 +727,12 @@ def build_renderer(
737
727
  if save_path:
738
728
  working_console = CapturingConsole(console, capture=True)
739
729
 
740
- # Decide live behavior: default is live unless verbose; allow explicit override
741
- live_enabled = (not verbose) if live is None else bool(live)
730
+ # Configure renderer based on verbose mode and explicit overrides
731
+ if live is None:
732
+ live_enabled = not verbose # Disable live mode in verbose (unless overridden)
733
+ else:
734
+ live_enabled = bool(live)
735
+
742
736
  renderer_cfg = RendererConfig(
743
737
  theme=theme,
744
738
  style="debug" if verbose else "pretty",
@@ -761,8 +755,119 @@ def build_renderer(
761
755
  return renderer, working_console
762
756
 
763
757
 
758
+ def _fuzzy_pick_for_resources(
759
+ resources: list[Any], resource_type: str, search_term: str
760
+ ) -> Any | None:
761
+ """
762
+ Fuzzy picker for resource objects, similar to _fuzzy_pick but without column dependencies.
763
+
764
+ Args:
765
+ resources: List of resource objects to choose from
766
+ resource_type: Type of resource (e.g., "agent", "tool")
767
+ search_term: The search term that led to multiple matches
768
+
769
+ Returns:
770
+ Selected resource object or None if cancelled/no selection
771
+ """
772
+ if not (_HAS_PTK and console.is_terminal and os.isatty(1)):
773
+ return None
774
+
775
+ # Build display corpus and a reverse map
776
+ labels = []
777
+ by_label: dict[str, Any] = {}
778
+ for resource in resources:
779
+ name = getattr(resource, "name", "Unknown")
780
+ _id = getattr(resource, "id", "Unknown")
781
+ # Create a display label similar to _row_display
782
+ label_parts = []
783
+ if name and name != "Unknown":
784
+ label_parts.append(name)
785
+ label_parts.append(f"[{_id[:8]}...]") # Show first 8 chars of ID
786
+ label = " • ".join(label_parts)
787
+
788
+ # Ensure uniqueness
789
+ if label in by_label:
790
+ i = 2
791
+ base = label
792
+ while f"{base} #{i}" in by_label:
793
+ i += 1
794
+ label = f"{base} #{i}"
795
+ labels.append(label)
796
+ by_label[label] = resource
797
+
798
+ # Create fuzzy completer
799
+ class FuzzyCompleter:
800
+ def __init__(self, words: list[str]):
801
+ self.words = words
802
+
803
+ def get_completions(self, document, complete_event):
804
+ word = document.get_word_before_cursor()
805
+ if not word:
806
+ return
807
+
808
+ word_lower = word.lower()
809
+ for label in self.words:
810
+ label_lower = label.lower()
811
+ # Fuzzy match logic
812
+ if self._fuzzy_match(word_lower, label_lower):
813
+ yield Completion(label, start_position=-len(word))
814
+
815
+ def _fuzzy_match(self, search: str, target: str) -> bool:
816
+ if not search:
817
+ return True
818
+
819
+ search_idx = 0
820
+ for char in target:
821
+ if search_idx < len(search) and search[search_idx] == char:
822
+ search_idx += 1
823
+ if search_idx == len(search):
824
+ return True
825
+ return False
826
+
827
+ completer = FuzzyCompleter(labels)
828
+
829
+ try:
830
+ answer = prompt(
831
+ message=f"Find 🤖 {resource_type.title()}: ",
832
+ completer=completer,
833
+ complete_in_thread=True,
834
+ complete_while_typing=True,
835
+ )
836
+ except (KeyboardInterrupt, EOFError):
837
+ return None
838
+
839
+ if not answer:
840
+ return None
841
+
842
+ # Exact label match
843
+ if answer in by_label:
844
+ return by_label[answer]
845
+
846
+ # Fuzzy search fallback
847
+ best_match = None
848
+ best_score = -1
849
+
850
+ for label in labels:
851
+ score = _fuzzy_score(answer.lower(), label.lower())
852
+ if score > best_score:
853
+ best_score = score
854
+ best_match = label
855
+
856
+ if best_match and best_score > 0:
857
+ return by_label[best_match]
858
+
859
+ return None
860
+
861
+
764
862
  def resolve_resource(
765
- ctx, ref: str, *, get_by_id, find_by_name, label: str, select: int | None = None
863
+ ctx,
864
+ ref: str,
865
+ *,
866
+ get_by_id,
867
+ find_by_name,
868
+ label: str,
869
+ select: int | None = None,
870
+ interface_preference: str = "fuzzy",
766
871
  ):
767
872
  """Resolve resource reference (ID or name) with ambiguity handling.
768
873
 
@@ -773,6 +878,7 @@ def resolve_resource(
773
878
  find_by_name: Function to find resources by name
774
879
  label: Resource type label for error messages
775
880
  select: Optional selection index for ambiguity resolution
881
+ interface_preference: "fuzzy" for fuzzy picker, "questionary" for up/down list
776
882
 
777
883
  Returns:
778
884
  Resolved resource object
@@ -795,7 +901,17 @@ def resolve_resource(
795
901
  raise click.ClickException(f"--select must be 1..{len(matches)}")
796
902
  return matches[idx]
797
903
 
798
- return handle_ambiguous_resource(ctx, label.lower(), ref, matches)
904
+ # Choose interface based on preference
905
+ if interface_preference == "fuzzy":
906
+ # Use fuzzy picker for modern UX
907
+ picked = _fuzzy_pick_for_resources(matches, label.lower(), ref)
908
+ if picked:
909
+ return picked
910
+ # Fallback to original ambiguity handler if fuzzy picker fails
911
+ return handle_ambiguous_resource(ctx, label.lower(), ref, matches)
912
+ else:
913
+ # Use questionary interface for traditional up/down selection
914
+ return handle_ambiguous_resource(ctx, label.lower(), ref, matches)
799
915
 
800
916
 
801
917
  def handle_ambiguous_resource(
@@ -825,7 +941,9 @@ def handle_ambiguous_resource(
825
941
 
826
942
  # Fallback numeric prompt
827
943
  console.print(
828
- f"[yellow]Multiple {resource_type.replace('{', '{{').replace('}', '}}')}s found matching '{ref.replace('{', '{{').replace('}', '}}')}':[/yellow]"
944
+ Text(
945
+ f"[yellow]Multiple {resource_type.replace('{', '{{').replace('}', '}}')}s found matching '{ref.replace('{', '{{').replace('}', '}}')}':[/yellow]"
946
+ )
829
947
  )
830
948
  table = Table(
831
949
  title=f"Select {resource_type.replace('{', '{{').replace('}', '}}').title()}",
@@ -31,6 +31,7 @@ from glaip_sdk.utils.client_utils import (
31
31
  )
32
32
  from glaip_sdk.utils.rendering.models import RunStats
33
33
  from glaip_sdk.utils.rendering.renderer import RichStreamRenderer
34
+ from glaip_sdk.utils.rendering.renderer.config import RendererConfig
34
35
 
35
36
  # Set up module-level logger
36
37
  logger = logging.getLogger("glaip_sdk.agents")
@@ -254,8 +255,28 @@ class AgentClient(BaseClient):
254
255
  if isinstance(renderer, RichStreamRenderer):
255
256
  r = renderer
256
257
  else:
257
- # Default to a standard rich renderer
258
- r = RichStreamRenderer(console=_Console())
258
+ # Check if verbose mode is requested
259
+ verbose = kwargs.get("verbose", False)
260
+ if verbose:
261
+ # Create a verbose renderer similar to CLI --verbose
262
+ verbose_config = RendererConfig(
263
+ theme="dark",
264
+ style="debug", # CLI uses "debug" style for verbose
265
+ live=False, # CLI disables live updates for verbose
266
+ show_delegate_tool_panels=True, # CLI always shows tool panels
267
+ append_finished_snapshots=False,
268
+ )
269
+ r = RichStreamRenderer(
270
+ console=_Console(),
271
+ cfg=verbose_config,
272
+ verbose=True,
273
+ )
274
+ else:
275
+ # Default to a standard rich renderer with tool panels enabled
276
+ default_config = RendererConfig(
277
+ show_delegate_tool_panels=True, # Enable tool panels by default
278
+ )
279
+ r = RichStreamRenderer(console=_Console(), cfg=default_config)
259
280
 
260
281
  # Try to set some meta early; refine as we receive events
261
282
  meta = {
glaip_sdk/client/tools.py CHANGED
@@ -49,67 +49,179 @@ class ToolClient(BaseClient):
49
49
  tools = create_model_instances(data, Tool, self)
50
50
  return find_by_name(tools, name, case_sensitive=False)
51
51
 
52
- def create_tool(
52
+ def _validate_and_read_file(self, file_path: str) -> str:
53
+ """Validate file exists and read its content.
54
+
55
+ Args:
56
+ file_path: Path to the file to read
57
+
58
+ Returns:
59
+ str: File content
60
+
61
+ Raises:
62
+ FileNotFoundError: If file doesn't exist
63
+ """
64
+ if not os.path.exists(file_path):
65
+ raise FileNotFoundError(f"Tool file not found: {file_path}")
66
+
67
+ with open(file_path, encoding="utf-8") as f:
68
+ return f.read()
69
+
70
+ def _extract_name_from_file(self, file_path: str) -> str:
71
+ """Extract tool name from file path.
72
+
73
+ Args:
74
+ file_path: Path to the file
75
+
76
+ Returns:
77
+ str: Extracted name (filename without extension)
78
+ """
79
+ return os.path.splitext(os.path.basename(file_path))[0]
80
+
81
+ def _prepare_upload_data(
82
+ self, name: str, framework: str, description: str | None = None, **kwargs
83
+ ) -> dict:
84
+ """Prepare upload data dictionary.
85
+
86
+ Args:
87
+ name: Tool name
88
+ framework: Tool framework
89
+ description: Optional description
90
+ **kwargs: Additional parameters
91
+
92
+ Returns:
93
+ dict: Upload data dictionary
94
+ """
95
+ data = {
96
+ "name": name,
97
+ "framework": framework,
98
+ }
99
+
100
+ if description:
101
+ data["description"] = description
102
+
103
+ # Handle tags if provided in kwargs
104
+ if "tags" in kwargs and kwargs["tags"]:
105
+ if isinstance(kwargs["tags"], list):
106
+ data["tags"] = ",".join(kwargs["tags"])
107
+ else:
108
+ data["tags"] = kwargs["tags"]
109
+
110
+ # Include any other kwargs in the upload data
111
+ for key, value in kwargs.items():
112
+ if key not in ["tags"]: # tags already handled above
113
+ data[key] = value
114
+
115
+ return data
116
+
117
+ def _upload_tool_file(self, file_path: str, upload_data: dict) -> Tool:
118
+ """Upload tool file to server.
119
+
120
+ Args:
121
+ file_path: Path to temporary file to upload
122
+ upload_data: Dictionary with upload metadata
123
+
124
+ Returns:
125
+ Tool: Created tool object
126
+ """
127
+ with open(file_path, "rb") as fb:
128
+ files = {
129
+ "file": (os.path.basename(file_path), fb, "application/octet-stream"),
130
+ }
131
+
132
+ response = self._request(
133
+ "POST",
134
+ "/tools/upload",
135
+ files=files,
136
+ data=upload_data,
137
+ )
138
+
139
+ return Tool(**response)._set_client(self)
140
+
141
+ def _create_tool_from_file(
53
142
  self,
143
+ file_path: str,
54
144
  name: str | None = None,
55
- tool_type: str = "custom",
56
145
  description: str | None = None,
57
- tool_script: str | None = None,
58
- tool_file: str | None = None,
59
- file_path: str | None = None,
60
- code: str | None = None,
61
146
  framework: str = "langchain",
62
147
  **kwargs,
63
148
  ) -> Tool:
64
- """Create a new tool.
149
+ """Create tool from file content using upload endpoint.
65
150
 
66
151
  Args:
67
- name: Tool name (required if not provided via file)
68
- tool_type: Tool type (defaults to "custom")
69
- description: Tool description (optional)
70
- tool_script: Tool script content (optional)
71
- tool_file: Tool file path (optional)
72
- file_path: Alternative to tool_file (for compatibility)
73
- code: Alternative to tool_script (for compatibility)
74
- framework: Tool framework (defaults to "langchain")
75
- **kwargs: Additional tool parameters
152
+ file_path: Path to tool file
153
+ name: Optional tool name (auto-detected if not provided)
154
+ description: Optional tool description
155
+ framework: Tool framework
156
+ **kwargs: Additional parameters
157
+
158
+ Returns:
159
+ Tool: Created tool object
76
160
  """
77
- # Handle compatibility parameters
78
- if file_path and not tool_file:
79
- tool_file = file_path
80
- if code and not tool_script:
81
- tool_script = code
161
+ # Read and validate file
162
+ file_content = self._validate_and_read_file(file_path)
82
163
 
83
- # Auto-detect name from file if not provided
84
- if not name and tool_file:
85
- import os
164
+ # Auto-detect name if not provided
165
+ if not name:
166
+ name = self._extract_name_from_file(file_path)
86
167
 
87
- name = os.path.splitext(os.path.basename(tool_file))[0]
168
+ # Handle description - generate default if not provided or empty
169
+ if description is None or description == "":
170
+ # Generate default description based on tool_type if available
171
+ tool_type = kwargs.get("tool_type", "custom")
172
+ description = f"A {tool_type} tool"
88
173
 
89
- if not name:
90
- raise ValueError(
91
- "Tool name is required (either explicitly or via file path)"
174
+ # Create temporary file for upload
175
+ with tempfile.NamedTemporaryFile(
176
+ mode="w",
177
+ suffix=".py",
178
+ prefix=f"{name}_",
179
+ delete=False,
180
+ encoding="utf-8",
181
+ ) as temp_file:
182
+ temp_file.write(file_content)
183
+ temp_file_path = temp_file.name
184
+
185
+ try:
186
+ # Prepare upload data
187
+ upload_data = self._prepare_upload_data(
188
+ name=name, framework=framework, description=description, **kwargs
92
189
  )
93
190
 
94
- # Auto-detect description if not provided
95
- if not description:
96
- description = f"A {tool_type} tool"
191
+ # Upload file
192
+ return self._upload_tool_file(temp_file_path, upload_data)
97
193
 
98
- payload = {
99
- "name": name,
100
- "tool_type": tool_type,
101
- "description": description,
102
- "framework": framework,
103
- **kwargs,
104
- }
194
+ finally:
195
+ # Clean up temporary file
196
+ try:
197
+ os.unlink(temp_file_path)
198
+ except OSError:
199
+ pass # Ignore cleanup errors
105
200
 
106
- if tool_script:
107
- payload["tool_script"] = tool_script
108
- if tool_file:
109
- payload["tool_file"] = tool_file
201
+ def create_tool(
202
+ self,
203
+ file_path: str,
204
+ name: str | None = None,
205
+ description: str | None = None,
206
+ framework: str = "langchain",
207
+ **kwargs,
208
+ ) -> Tool:
209
+ """Create a new tool from a file.
110
210
 
111
- data = self._request("POST", "/tools/", json=payload)
112
- return Tool(**data)._set_client(self)
211
+ Args:
212
+ file_path: File path to tool script (required) - file content will be read and processed as plugin
213
+ name: Tool name (auto-detected from file if not provided)
214
+ description: Tool description (auto-generated if not provided)
215
+ framework: Tool framework (defaults to "langchain")
216
+ **kwargs: Additional tool parameters
217
+ """
218
+ return self._create_tool_from_file(
219
+ file_path=file_path,
220
+ name=name,
221
+ description=description,
222
+ framework=framework,
223
+ **kwargs,
224
+ )
113
225
 
114
226
  def create_tool_from_code(
115
227
  self,
@@ -129,41 +241,35 @@ class ToolClient(BaseClient):
129
241
  name: Name for the tool (used for temporary file naming)
130
242
  code: Python code containing the tool plugin
131
243
  framework: Tool framework (defaults to "langchain")
244
+ description: Optional tool description
245
+ tags: Optional list of tags
132
246
 
133
247
  Returns:
134
248
  Tool: The created tool object
135
249
  """
136
250
  # Create a temporary file with the tool code
137
251
  with tempfile.NamedTemporaryFile(
138
- mode="w", suffix=".py", prefix=f"{name}_", delete=False, encoding="utf-8"
252
+ mode="w",
253
+ suffix=".py",
254
+ prefix=f"{name}_",
255
+ delete=False,
256
+ encoding="utf-8",
139
257
  ) as temp_file:
140
258
  temp_file.write(code)
141
259
  temp_file_path = temp_file.name
142
260
 
143
261
  try:
144
- # Prepare multipart upload
145
- filename = os.path.basename(temp_file_path)
146
- with open(temp_file_path, "rb") as fb:
147
- files = {
148
- "file": (filename, fb, "application/octet-stream"),
149
- }
150
- data = {
151
- "name": name,
152
- "framework": framework,
153
- }
154
- if description:
155
- data["description"] = description
156
- if tags:
157
- # Backend might expect comma-separated or JSON; start with comma-separated
158
- data["tags"] = ",".join(tags)
262
+ # Prepare upload data using shared helper
263
+ upload_data = self._prepare_upload_data(
264
+ name=name,
265
+ framework=framework,
266
+ description=description,
267
+ tags=tags if tags else None,
268
+ )
269
+
270
+ # Upload file using shared helper
271
+ return self._upload_tool_file(temp_file_path, upload_data)
159
272
 
160
- response = self._request(
161
- "POST",
162
- "/tools/upload",
163
- files=files,
164
- data=data,
165
- )
166
- return Tool(**response)._set_client(self)
167
273
  finally:
168
274
  # Clean up the temporary file
169
275
  try:
@@ -180,24 +286,6 @@ class ToolClient(BaseClient):
180
286
  """Delete a tool."""
181
287
  self._request("DELETE", f"/tools/{tool_id}")
182
288
 
183
- def install_tool(self, tool_id: str) -> bool:
184
- """Install a tool."""
185
- try:
186
- self._request("POST", f"/tools/{tool_id}/install")
187
- return True
188
- except Exception as e:
189
- logger.error(f"Failed to install tool {tool_id}: {e}")
190
- return False
191
-
192
- def uninstall_tool(self, tool_id: str) -> bool:
193
- """Uninstall a tool."""
194
- try:
195
- self._request("POST", f"/tools/{tool_id}/uninstall")
196
- return True
197
- except Exception as e:
198
- logger.error(f"Failed to install tool {tool_id}: {e}")
199
- return False
200
-
201
289
  def get_tool_script(self, tool_id: str) -> str:
202
290
  """Get the tool script content.
203
291
 
@@ -232,29 +320,29 @@ class ToolClient(BaseClient):
232
320
  FileNotFoundError: If the file doesn't exist
233
321
  Exception: If the update fails
234
322
  """
235
- import os
236
-
237
- if not os.path.exists(file_path):
238
- raise FileNotFoundError(f"Tool file not found: {file_path}")
323
+ # Validate file exists
324
+ self._validate_and_read_file(file_path)
239
325
 
240
326
  try:
241
327
  # Prepare multipart upload
242
- filename = os.path.basename(file_path)
243
328
  with open(file_path, "rb") as fb:
244
329
  files = {
245
- "file": (filename, fb, "application/octet-stream"),
330
+ "file": (
331
+ os.path.basename(file_path),
332
+ fb,
333
+ "application/octet-stream",
334
+ ),
246
335
  }
247
336
 
248
- # Add any additional metadata
249
- data = kwargs.copy()
250
-
251
337
  response = self._request(
252
338
  "PUT",
253
339
  f"/tools/{tool_id}/upload",
254
340
  files=files,
255
- data=data,
341
+ data=kwargs, # Pass kwargs directly as data
256
342
  )
343
+
257
344
  return Tool(**response)._set_client(self)
345
+
258
346
  except Exception as e:
259
347
  logger.error(f"Failed to update tool {tool_id} via file: {e}")
260
348
  raise
glaip_sdk/models.py CHANGED
@@ -128,7 +128,7 @@ class Tool(BaseModel):
128
128
  raise RuntimeError(
129
129
  "No client available. Use client.get_tool_by_id() to get a client-connected tool."
130
130
  )
131
- self._client.tools.delete_tool(self.id)
131
+ self._client.delete_tool(self.id)
132
132
 
133
133
 
134
134
  class MCP(BaseModel):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: glaip-sdk
3
- Version: 0.0.3
3
+ Version: 0.0.4
4
4
  Summary: Python SDK for AI Agent Platform - Simplified CLI Design
5
5
  Author-email: Raymond Christopher <raymond.christopher@gdplabs.id>
6
6
  License: MIT
@@ -1,22 +1,23 @@
1
1
  glaip_sdk/__init__.py,sha256=UGBsYHHSgSc1HGCTsA9bbz2ARJJ1g49cTieXEINHtAk,323
2
2
  glaip_sdk/_version.py,sha256=Rb9YLDvK1DXCVFrjlLDbtucpwKh_PyCnmZ-ia9VX3Cc,1650
3
+ glaip_sdk/branding.py,sha256=Iwm9qsPoRNCiv7IS_pet_vno8ltdVR4UwyEHoYH7qp0,4891
3
4
  glaip_sdk/exceptions.py,sha256=QTVtwxRHMN4e8gGn0icXphZvdugiRvcSrlMYylwGsDc,1993
4
- glaip_sdk/models.py,sha256=i_MumCH0PvqEJLsUrGm7vkytxnhsJ3YtWq2y6H-JVzQ,7235
5
+ glaip_sdk/models.py,sha256=kkuH66cj5Oyrma3bLldPpheeXR4H9xZa_z32bpLd3DU,7229
5
6
  glaip_sdk/cli/__init__.py,sha256=cPI-uOOBww_ESiH6NQBdJiTg8CUVNigFOU3kl0tgTuI,143
6
- glaip_sdk/cli/main.py,sha256=Xx7kNbnWV--oLh-DxfR94a2rGecziwi_JHIFkwEqj70,10133
7
- glaip_sdk/cli/utils.py,sha256=PeVsG84-i1UbX-ETDUPrrLuismRQZTJLQdBvOqWia6Q,25540
7
+ glaip_sdk/cli/main.py,sha256=jiZmj9smpFYkdToJip_JMBNoBEIqOrt1cTaTQrNmN7w,10557
8
+ glaip_sdk/cli/utils.py,sha256=UBIrLIKLi4UZjwEa8J1AiOyGJe8o6eVhYBCwjx51CxQ,29182
8
9
  glaip_sdk/cli/commands/__init__.py,sha256=WShiuYqHcbuexPboibDJ_Q2jOPIWp-TgsyUAO2-T20I,134
9
- glaip_sdk/cli/commands/agents.py,sha256=5-bhSo_lXFhhpJRAPd9Z0lxQq4Yg_OImRBVwk-debko,17331
10
- glaip_sdk/cli/commands/configure.py,sha256=2MDQ5Q-Rrd7T3eO8ThKJZz_B3-Gqp3UFKk-rrnmPPvk,7135
11
- glaip_sdk/cli/commands/init.py,sha256=Er8UH2aMpnLJ9aiSBOfp26__G1ll5L7W-CYaiHpats8,5778
12
- glaip_sdk/cli/commands/mcps.py,sha256=09KlY9ICjinuTkZvLKDsyOT3Ahz4zk_LJPZqhJrc9Lk,11956
10
+ glaip_sdk/cli/commands/agents.py,sha256=yNrlOooe9vx_A_frycdJ9U4aibXAHV4aYwVYn54RbSY,38515
11
+ glaip_sdk/cli/commands/configure.py,sha256=SNaWPO0bgI6bztlKgoqGr0qh2KSSDztyubKhrw_HLEI,7285
12
+ glaip_sdk/cli/commands/init.py,sha256=W4z5TBlfSn9fmHpH1W92llgqfF0OvhgJSdny-P61Y_4,2934
13
+ glaip_sdk/cli/commands/mcps.py,sha256=4Ny54ApHYFCkJCXORD7zslVnrNdKs981adIbJNaYAMQ,12099
13
14
  glaip_sdk/cli/commands/models.py,sha256=j8VqQ2edSEvD-sQXWm8ifUco9gX8J-OBib6OvzthpqU,1405
14
- glaip_sdk/cli/commands/tools.py,sha256=0YVJ7Z4g5Saor2M0jCp2B2BzkgfVaE5Wr4S-7oSpV6U,14397
15
+ glaip_sdk/cli/commands/tools.py,sha256=-xPMOKD-shI2ly9Tl-IwhKQR2kEekEs63bw7EEXNoAU,14508
15
16
  glaip_sdk/client/__init__.py,sha256=jnsD3HCiOGiuL_4aqJiVnBsF8HNFS-s_aNLfg26gJqs,7530
16
- glaip_sdk/client/agents.py,sha256=PF-4HF2gx8E3uA5Xx6rQcEzKI9svaYgJPBuO8Ze-wdQ,14000
17
+ glaip_sdk/client/agents.py,sha256=k3QthlIu82KeXV_W29LuFTGZVzyGL11ZY5OA4P4a2Ug,15041
17
18
  glaip_sdk/client/base.py,sha256=3Ri5GVYrZ4cZ5lex1pyG0nvEmGxeXwAY194MxSH1_cY,9463
18
19
  glaip_sdk/client/mcps.py,sha256=UEwgFbl4ogeozfJGcUbQQ7JGfwYliN9i5V6fTvkbOZ0,4311
19
- glaip_sdk/client/tools.py,sha256=RMSDFeaV0u_iE331d66hkinz4a1_GWfBRbyrOEejvYA,8610
20
+ glaip_sdk/client/tools.py,sha256=v5jxHPgoGHwB2PXzlm0yywvqKaYLcrm6dJz9aT1SxuM,10954
20
21
  glaip_sdk/client/validators.py,sha256=3MtOt11IivEwQAgzmdRGWUBzP223ytECOLU_a77x7IU,7101
21
22
  glaip_sdk/config/constants.py,sha256=Pm0tGYiJ9VdW9XXz0CG36l8-sAhnlFPTKbHKKleUgik,705
22
23
  glaip_sdk/utils/__init__.py,sha256=5pOPBTjJ4hzvMNYjLnTut_ovwU8QJ39kARKx9-qr5Qs,5101
@@ -34,7 +35,7 @@ glaip_sdk/utils/rendering/renderer/debug.py,sha256=2XP6A4w3EIjVVY_M-BDPvHf05QQSz
34
35
  glaip_sdk/utils/rendering/renderer/panels.py,sha256=iTDJoRBaa73pX9z9nTcb6aKhfXe1Mds0RzCuhwtvkdc,2994
35
36
  glaip_sdk/utils/rendering/renderer/progress.py,sha256=i4HG_iNwtK4c3Gf2sviLCiOJ-5ydX4t-YE5dgtLOxNI,3438
36
37
  glaip_sdk/utils/rendering/renderer/stream.py,sha256=ZKitYkt_7OirNeERu3cc_NybQ8mWUMoEstg9UL1S090,7323
37
- glaip_sdk-0.0.3.dist-info/METADATA,sha256=Rtei3FvfrthFvPqL3t1biMy6zvxiwAsCyJi_iB-6KHk,19501
38
- glaip_sdk-0.0.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
39
- glaip_sdk-0.0.3.dist-info/entry_points.txt,sha256=65vNPUggyYnVGhuw7RhNJ8Fp2jygTcX0yxJBcBY3iLU,48
40
- glaip_sdk-0.0.3.dist-info/RECORD,,
38
+ glaip_sdk-0.0.4.dist-info/METADATA,sha256=H5uB6CuucGboUl_Z9LhzJHOIc7PS_a0pgHcqNX7-ANE,19501
39
+ glaip_sdk-0.0.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
40
+ glaip_sdk-0.0.4.dist-info/entry_points.txt,sha256=65vNPUggyYnVGhuw7RhNJ8Fp2jygTcX0yxJBcBY3iLU,48
41
+ glaip_sdk-0.0.4.dist-info/RECORD,,