airtrain 0.1.58__py3-none-any.whl → 0.1.62__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.
@@ -0,0 +1,450 @@
1
+ """
2
+ Search tools for AirTrain agents.
3
+
4
+ This module provides tools for searching for content within files and directories.
5
+ """
6
+
7
+ import os
8
+ import re
9
+ import subprocess
10
+ from typing import Dict, Any, List, Optional, cast
11
+
12
+ from .registry import StatelessTool, register_tool
13
+ from airtrain.integrations.search.exa import (
14
+ ExaCredentials,
15
+ ExaSearchSkill,
16
+ ExaSearchInputSchema,
17
+ ExaSearchOutputSchema,
18
+ ExaContentConfig,
19
+ )
20
+
21
+
22
+ @register_tool("search_term")
23
+ class SearchTermTool(StatelessTool):
24
+ """Tool for searching for specific terms within files."""
25
+
26
+ def __init__(self):
27
+ self.name = "search_term"
28
+ self.description = "Search for a specific term or pattern within files"
29
+ self.parameters = {
30
+ "type": "object",
31
+ "properties": {
32
+ "term": {
33
+ "type": "string",
34
+ "description": "The term or pattern to search for",
35
+ },
36
+ "directory": {
37
+ "type": "string",
38
+ "description": "Directory to search in (defaults to current directory)",
39
+ },
40
+ "file_pattern": {
41
+ "type": "string",
42
+ "description": "Pattern to filter files (e.g., *.py, *.txt)",
43
+ },
44
+ "case_sensitive": {
45
+ "type": "boolean",
46
+ "description": "Whether the search should be case-sensitive",
47
+ },
48
+ "regex": {
49
+ "type": "boolean",
50
+ "description": "Whether to treat the term as a regular expression",
51
+ },
52
+ "max_results": {
53
+ "type": "integer",
54
+ "description": "Maximum number of results to return",
55
+ },
56
+ "max_context_lines": {
57
+ "type": "integer",
58
+ "description": "Number of context lines to show before and after matches",
59
+ },
60
+ },
61
+ "required": ["term"],
62
+ }
63
+
64
+ def __call__(
65
+ self,
66
+ term: str,
67
+ directory: str = ".",
68
+ file_pattern: str = "*",
69
+ case_sensitive: bool = False,
70
+ regex: bool = False,
71
+ max_results: int = 100,
72
+ max_context_lines: int = 2,
73
+ ) -> Dict[str, Any]:
74
+ """Search for a specific term within files."""
75
+ try:
76
+ # Try to use grep if available (more efficient than pure Python)
77
+ try:
78
+ return self._search_with_grep(
79
+ term,
80
+ directory,
81
+ file_pattern,
82
+ case_sensitive,
83
+ regex,
84
+ max_results,
85
+ max_context_lines,
86
+ )
87
+ except (subprocess.SubprocessError, FileNotFoundError):
88
+ # Fall back to Python implementation if grep is not available
89
+ return self._search_with_python(
90
+ term,
91
+ directory,
92
+ file_pattern,
93
+ case_sensitive,
94
+ regex,
95
+ max_results,
96
+ max_context_lines,
97
+ )
98
+ except Exception as e:
99
+ return {"success": False, "error": f"Error searching for term: {str(e)}"}
100
+
101
+ def _search_with_grep(
102
+ self,
103
+ term: str,
104
+ directory: str,
105
+ file_pattern: str,
106
+ case_sensitive: bool,
107
+ regex: bool,
108
+ max_results: int,
109
+ max_context_lines: int,
110
+ ) -> Dict[str, Any]:
111
+ """Use grep to search for terms (more efficient)."""
112
+ # Prepare grep command
113
+ cmd = ["grep"]
114
+
115
+ # Add grep options
116
+ if not case_sensitive:
117
+ cmd.append("-i") # Case insensitive
118
+
119
+ if not regex:
120
+ cmd.append("-F") # Fixed string (not regex)
121
+
122
+ # Add context lines
123
+ if max_context_lines > 0:
124
+ cmd.append(f"-C{max_context_lines}") # Context lines
125
+
126
+ # Add recursive search
127
+ cmd.append("-r")
128
+
129
+ # Add line numbers
130
+ cmd.append("-n")
131
+
132
+ # Add max count if specified
133
+ if max_results > 0:
134
+ cmd.append(f"--max-count={max_results}")
135
+
136
+ # Add search term
137
+ cmd.append(term)
138
+
139
+ # Add directory
140
+ cmd.append(directory)
141
+
142
+ # Add file pattern if specified
143
+ if file_pattern != "*":
144
+ cmd.append("--include")
145
+ cmd.append(file_pattern)
146
+
147
+ # Execute grep command
148
+ result = subprocess.run(cmd, capture_output=True, text=True)
149
+
150
+ # Process results
151
+ if result.returncode != 0 and result.returncode != 1: # 1 means no matches
152
+ raise subprocess.SubprocessError(f"Grep error: {result.stderr}")
153
+
154
+ # Parse output
155
+ matches = []
156
+ for line in result.stdout.splitlines():
157
+ if not line.strip():
158
+ continue
159
+
160
+ # Parse grep output (filename:line_number:content)
161
+ parts = line.split(":", 2)
162
+ if len(parts) >= 3:
163
+ filename = parts[0]
164
+ line_number = int(parts[1])
165
+ content = parts[2]
166
+
167
+ matches.append(
168
+ {"file": filename, "line": line_number, "content": content}
169
+ )
170
+
171
+ return {
172
+ "success": True,
173
+ "term": term,
174
+ "directory": directory,
175
+ "file_pattern": file_pattern,
176
+ "matches": matches,
177
+ "match_count": len(matches),
178
+ "truncated": result.stdout.count("\n") >= max_results,
179
+ }
180
+
181
+ def _search_with_python(
182
+ self,
183
+ term: str,
184
+ directory: str,
185
+ file_pattern: str,
186
+ case_sensitive: bool,
187
+ regex: bool,
188
+ max_results: int,
189
+ max_context_lines: int,
190
+ ) -> Dict[str, Any]:
191
+ """Use Python to search for terms (fallback method)."""
192
+ import fnmatch
193
+ import glob
194
+
195
+ # Expand directory path
196
+ directory = os.path.expanduser(directory)
197
+
198
+ if not os.path.exists(directory):
199
+ return {
200
+ "success": False,
201
+ "error": f"Directory '{directory}' does not exist",
202
+ }
203
+
204
+ if not os.path.isdir(directory):
205
+ return {"success": False, "error": f"Path '{directory}' is not a directory"}
206
+
207
+ # Compile regex if needed
208
+ if regex:
209
+ if case_sensitive:
210
+ pattern = re.compile(term)
211
+ else:
212
+ pattern = re.compile(term, re.IGNORECASE)
213
+ else:
214
+ if case_sensitive:
215
+ pattern = re.compile(re.escape(term))
216
+ else:
217
+ pattern = re.compile(re.escape(term), re.IGNORECASE)
218
+
219
+ # Find all files matching the pattern
220
+ matches = []
221
+ match_count = 0
222
+ truncated = False
223
+
224
+ for root, _, files in os.walk(directory):
225
+ for filename in fnmatch.filter(files, file_pattern):
226
+ file_path = os.path.join(root, filename)
227
+
228
+ try:
229
+ with open(file_path, "r", encoding="utf-8", errors="ignore") as f:
230
+ lines = f.readlines()
231
+
232
+ # Search for matches in the file
233
+ for i, line in enumerate(lines):
234
+ if pattern.search(line):
235
+ line_number = i + 1
236
+
237
+ # Extract context lines
238
+ context_start = max(0, i - max_context_lines)
239
+ context_end = min(len(lines), i + max_context_lines + 1)
240
+ context_lines = lines[context_start:context_end]
241
+
242
+ matches.append(
243
+ {
244
+ "file": file_path,
245
+ "line": line_number,
246
+ "content": line.rstrip("\n"),
247
+ "context": {
248
+ "start_line": context_start + 1,
249
+ "end_line": context_end,
250
+ "lines": [
251
+ l.rstrip("\n") for l in context_lines
252
+ ],
253
+ },
254
+ }
255
+ )
256
+
257
+ match_count += 1
258
+ if match_count >= max_results:
259
+ truncated = True
260
+ break
261
+
262
+ if truncated:
263
+ break
264
+
265
+ except Exception as e:
266
+ # Skip files that can't be read
267
+ continue
268
+
269
+ if truncated:
270
+ break
271
+
272
+ return {
273
+ "success": True,
274
+ "term": term,
275
+ "directory": directory,
276
+ "file_pattern": file_pattern,
277
+ "matches": matches,
278
+ "match_count": match_count,
279
+ "truncated": truncated,
280
+ }
281
+
282
+ def to_dict(self):
283
+ """Convert tool to dictionary format for LLM function calling."""
284
+ return {
285
+ "type": "function",
286
+ "function": {
287
+ "name": self.name,
288
+ "description": self.description,
289
+ "parameters": self.parameters,
290
+ },
291
+ }
292
+
293
+
294
+ @register_tool("web_search")
295
+ class WebSearchTool(StatelessTool):
296
+ """Tool for searching the web using the Exa API."""
297
+
298
+ def __init__(self):
299
+ self.name = "web_search"
300
+ self.description = "Search the web for information using the Exa search API"
301
+ self.parameters = {
302
+ "type": "object",
303
+ "properties": {
304
+ "query": {
305
+ "type": "string",
306
+ "description": "The search query to execute",
307
+ },
308
+ "num_results": {
309
+ "type": "integer",
310
+ "description": "Number of results to return (default: 5)",
311
+ },
312
+ "include_domains": {
313
+ "type": "array",
314
+ "items": {"type": "string"},
315
+ "description": "List of domains to include in the search",
316
+ },
317
+ "exclude_domains": {
318
+ "type": "array",
319
+ "items": {"type": "string"},
320
+ "description": "List of domains to exclude from the search",
321
+ },
322
+ "use_autoprompt": {
323
+ "type": "boolean",
324
+ "description": "Whether to use Exa's autoprompt feature for better results",
325
+ },
326
+ },
327
+ "required": ["query"],
328
+ }
329
+
330
+ # Exa API key from environment variable
331
+ api_key = os.environ.get("EXA_API_KEY", "")
332
+ if api_key:
333
+ self.credentials = ExaCredentials(api_key=api_key)
334
+ self.skill = ExaSearchSkill(credentials=self.credentials)
335
+ else:
336
+ self.credentials = None
337
+ self.skill = None
338
+
339
+ async def _async_search(
340
+ self,
341
+ query: str,
342
+ num_results: int = 5,
343
+ include_domains: Optional[List[str]] = None,
344
+ exclude_domains: Optional[List[str]] = None,
345
+ use_autoprompt: bool = False,
346
+ ) -> Dict[str, Any]:
347
+ """Execute the search asynchronously."""
348
+ if not self.credentials or not self.skill:
349
+ return {
350
+ "success": False,
351
+ "error": "Exa API key not configured. Set the EXA_API_KEY environment variable.",
352
+ }
353
+
354
+ try:
355
+ # Create input for the search
356
+ search_input = ExaSearchInputSchema(
357
+ query=query,
358
+ numResults=num_results,
359
+ includeDomains=include_domains,
360
+ excludeDomains=exclude_domains,
361
+ useAutoprompt=use_autoprompt,
362
+ contents=ExaContentConfig(text=True),
363
+ )
364
+
365
+ # Execute search
366
+ result = await self.skill.process(search_input)
367
+
368
+ # Process results into a simplified format
369
+ search_results = []
370
+ for item in result.results:
371
+ search_results.append(
372
+ {
373
+ "title": item.title or "No title",
374
+ "url": item.url,
375
+ "content": (
376
+ item.text[:1000] if item.text else "No content available"
377
+ ),
378
+ "score": item.score,
379
+ "published": item.published,
380
+ }
381
+ )
382
+
383
+ return {
384
+ "success": True,
385
+ "query": query,
386
+ "results": search_results,
387
+ "result_count": len(search_results),
388
+ "autoprompt": result.autopromptString,
389
+ }
390
+
391
+ except Exception as e:
392
+ return {"success": False, "error": f"Error performing web search: {str(e)}"}
393
+
394
+ def __call__(
395
+ self,
396
+ query: str,
397
+ num_results: int = 5,
398
+ include_domains: Optional[List[str]] = None,
399
+ exclude_domains: Optional[List[str]] = None,
400
+ use_autoprompt: bool = False,
401
+ ) -> Dict[str, Any]:
402
+ """
403
+ Search the web for information.
404
+
405
+ Args:
406
+ query: The search query to execute
407
+ num_results: Number of results to return
408
+ include_domains: List of domains to include in search results
409
+ exclude_domains: List of domains to exclude from search results
410
+ use_autoprompt: Whether to use Exa's autoprompt feature
411
+
412
+ Returns:
413
+ Dictionary containing search results or error information
414
+ """
415
+ import asyncio
416
+
417
+ if not self.credentials or not self.skill:
418
+ return {
419
+ "success": False,
420
+ "error": "Exa API key not configured. Set the EXA_API_KEY environment variable.",
421
+ }
422
+
423
+ # Run the async search in a new event loop
424
+ try:
425
+ loop = asyncio.new_event_loop()
426
+ asyncio.set_event_loop(loop)
427
+ result = loop.run_until_complete(
428
+ self._async_search(
429
+ query=query,
430
+ num_results=num_results,
431
+ include_domains=include_domains,
432
+ exclude_domains=exclude_domains,
433
+ use_autoprompt=use_autoprompt,
434
+ )
435
+ )
436
+ loop.close()
437
+ return result
438
+ except Exception as e:
439
+ return {"success": False, "error": f"Error executing search: {str(e)}"}
440
+
441
+ def to_dict(self):
442
+ """Convert tool to dictionary format for LLM function calling."""
443
+ return {
444
+ "type": "function",
445
+ "function": {
446
+ "name": self.name,
447
+ "description": self.description,
448
+ "parameters": self.parameters,
449
+ },
450
+ }
@@ -0,0 +1,135 @@
1
+ """
2
+ Testing tools for AirTrain agents.
3
+
4
+ This module provides tools for running tests and test frameworks.
5
+ """
6
+
7
+ import os
8
+ import subprocess
9
+ from typing import Dict, Any, Optional, List
10
+
11
+ from .registry import StatelessTool, register_tool
12
+
13
+
14
+ @register_tool("run_pytest")
15
+ class RunPytestTool(StatelessTool):
16
+ """Tool for running Python pytest on test files."""
17
+
18
+ def __init__(self):
19
+ self.name = "run_pytest"
20
+ self.description = "Run pytest on a specific test file or directory"
21
+ self.parameters = {
22
+ "type": "object",
23
+ "properties": {
24
+ "test_path": {
25
+ "type": "string",
26
+ "description": "Path to the test file or directory to run",
27
+ },
28
+ "args": {
29
+ "type": "array",
30
+ "items": {"type": "string"},
31
+ "description": "Additional pytest arguments",
32
+ },
33
+ "working_dir": {
34
+ "type": "string",
35
+ "description": "Working directory to run pytest from",
36
+ },
37
+ "verbose": {
38
+ "type": "boolean",
39
+ "description": "Run tests in verbose mode",
40
+ },
41
+ "capture_output": {
42
+ "type": "boolean",
43
+ "description": "Capture stdout/stderr or show directly",
44
+ },
45
+ "timeout": {
46
+ "type": "number",
47
+ "description": "Timeout in seconds for the test run",
48
+ },
49
+ },
50
+ "required": ["test_path"],
51
+ }
52
+
53
+ def __call__(
54
+ self,
55
+ test_path: str,
56
+ args: Optional[List[str]] = None,
57
+ working_dir: Optional[str] = None,
58
+ verbose: bool = False,
59
+ capture_output: bool = True,
60
+ timeout: float = 60.0,
61
+ ) -> Dict[str, Any]:
62
+ """Run pytest on a specific test file or directory."""
63
+ try:
64
+ # Expand user path if present
65
+ test_path = os.path.expanduser(test_path)
66
+
67
+ # Validate test path
68
+ if not os.path.exists(test_path):
69
+ return {
70
+ "success": False,
71
+ "error": f"Test path '{test_path}' does not exist",
72
+ }
73
+
74
+ # Prepare pytest command
75
+ cmd = ["pytest", test_path]
76
+
77
+ # Add verbosity flag if requested
78
+ if verbose:
79
+ cmd.append("-v")
80
+
81
+ # Add any additional arguments
82
+ if args:
83
+ cmd.extend(args)
84
+
85
+ # Run pytest
86
+ process = subprocess.run(
87
+ cmd,
88
+ cwd=working_dir,
89
+ capture_output=capture_output,
90
+ text=True,
91
+ timeout=timeout,
92
+ )
93
+
94
+ result = {
95
+ "success": process.returncode == 0,
96
+ "return_code": process.returncode,
97
+ "test_path": test_path,
98
+ "command": " ".join(cmd),
99
+ }
100
+
101
+ if capture_output:
102
+ result["stdout"] = process.stdout
103
+ result["stderr"] = process.stderr
104
+
105
+ # Parse test summary from output
106
+ if "failed" in process.stdout or "passed" in process.stdout:
107
+ summary_lines = []
108
+ for line in process.stdout.splitlines():
109
+ if "failed" in line or "passed" in line or "skipped" in line:
110
+ if "===" in line and "===" in line:
111
+ summary_lines.append(line.strip())
112
+
113
+ if summary_lines:
114
+ result["summary"] = summary_lines
115
+
116
+ return result
117
+
118
+ except subprocess.TimeoutExpired:
119
+ return {
120
+ "success": False,
121
+ "error": f"Pytest run timed out after {timeout} seconds",
122
+ }
123
+ except Exception as e:
124
+ return {"success": False, "error": f"Error running pytest: {str(e)}"}
125
+
126
+ def to_dict(self):
127
+ """Convert tool to dictionary format for LLM function calling."""
128
+ return {
129
+ "type": "function",
130
+ "function": {
131
+ "name": self.name,
132
+ "description": self.description,
133
+ "parameters": self.parameters,
134
+ },
135
+ }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: airtrain
3
- Version: 0.1.58
3
+ Version: 0.1.62
4
4
  Summary: A platform for building and deploying AI agents with structured skills
5
5
  Home-page: https://github.com/rosaboyle/airtrain.dev
6
6
  Author: Dheeraj Pai
@@ -1,6 +1,6 @@
1
- airtrain/__init__.py,sha256=8g5IGtnihwqBfltBMs9vMLsTNsnhwiYSXlWBkIOpqC0,3312
1
+ airtrain/__init__.py,sha256=gTO8GEuN1gIKjo5HlBOURfVnRtEuv61zF-HMxTXQYQk,3357
2
2
  airtrain/__main__.py,sha256=EU8ffFmCdC1G-UcHHt0Oo3lB1PGqfC6kwzH39CnYSwU,72
3
- airtrain/__pycache__/__init__.cpython-313.pyc,sha256=b5Xwx91d-BMvzIS-P4uZK8btsx2_mlxfUxvtoT4pVeA,3000
3
+ airtrain/__pycache__/__init__.cpython-313.pyc,sha256=gNzuKeI25qKWqhRq6MRzf8Otl1qJEekuX_QU0HAx7bE,2759
4
4
  airtrain/agents/__init__.py,sha256=r6v5_bblxamRgiaCT8CVhyzaDdWohGM7sSjLgIUpA5s,795
5
5
  airtrain/agents/example_agent.py,sha256=0dCS8QXIvUYYkxwyOEMLMdlQ4KgMAerQ56r7NcYGqTw,11681
6
6
  airtrain/agents/groq_agent.py,sha256=s-f-cGSrgsR4Jlrv6xzeg2Z0LAEHByhFMYVbTvu-Bkg,10558
@@ -16,13 +16,13 @@ airtrain/contrib/travel/__init__.py,sha256=clmBodw4nkTA-DsgjVGcXfJGPaWxIpCZDtdO-
16
16
  airtrain/contrib/travel/agents.py,sha256=tpQtZ0WUiXBuhvZtc2JlEam5TuR5l-Tndi14YyImDBM,8975
17
17
  airtrain/contrib/travel/models.py,sha256=E4Mtds5TINmXLXu65aTYqv6wwOh2KclJHZ2eXRrBqiY,1547
18
18
  airtrain/core/__init__.py,sha256=9h7iKwTzZocCPc9bU6j8bA02BokteWIOcO1uaqGMcrk,254
19
- airtrain/core/credentials.py,sha256=PgQotrQc46J5djidKnkK1znUv3fyNkUFDO-m2Kn_Gzo,4006
19
+ airtrain/core/credentials.py,sha256=J1jd8vLrOfet0GhLI1J44d35o7skjriBsMgpODmXwfo,5592
20
20
  airtrain/core/schemas.py,sha256=MMXrDviC4gRea_QaPpbjgO--B_UKxnD7YrxqZOLJZZU,7003
21
21
  airtrain/core/skills.py,sha256=kIkI1MwIzuAwyoxdnZ5MGOb70BOB-njbVb_csJEdVvc,9244
22
- airtrain/core/__pycache__/__init__.cpython-313.pyc,sha256=DEcKhvQGoYLFbdtCs9Fqp8sKXh8xM_viOh0drOix1cY,534
23
- airtrain/core/__pycache__/schemas.cpython-313.pyc,sha256=DX72FLcg5BWaj-xv6yjPFZvrhYhQY7jte89gIrNGEw0,8327
24
- airtrain/core/__pycache__/skills.cpython-313.pyc,sha256=lKR7C-bY2uS4Ad12UNSO8MLVxU-52OLJPC-nzV4u79w,11839
25
- airtrain/integrations/__init__.py,sha256=Y0yJt6c6uKOkMXdGPELSc6NYiLrEYr_38acViNPXWNk,2046
22
+ airtrain/core/__pycache__/__init__.cpython-313.pyc,sha256=aHwrmX5Hf3s9pSa8V0PH3ytqldRjB_X9AWwA6IDpzyk,534
23
+ airtrain/core/__pycache__/schemas.cpython-313.pyc,sha256=WpKRuARzTVGt_NDMApl8GNIQgF_jZcHV34z-KYs3g5g,8327
24
+ airtrain/core/__pycache__/skills.cpython-313.pyc,sha256=4EppdsB6rISIJNF8CbyCPHXlMCX1JKETeKxOS8hENLw,11839
25
+ airtrain/integrations/__init__.py,sha256=nGG3gZJ_sEaMzi_utsQCzExgnpHUjua8L9TyzofLQnw,2842
26
26
  airtrain/integrations/anthropic/__init__.py,sha256=K741w3v7fWsCknTo38ARqDL0D3HPlwDIvDuuBao9Tto,800
27
27
  airtrain/integrations/anthropic/credentials.py,sha256=hlTSw9HX66kYNaeQUtn0JjdZQBMNkzzFOJOoLOOzvcY,1246
28
28
  airtrain/integrations/anthropic/list_models.py,sha256=o7FABp0Cq3gs76zOF-CM9ohmYslWT6vK9qtQabV9XzI,3973
@@ -36,7 +36,7 @@ airtrain/integrations/cerebras/credentials.py,sha256=KDEH4r8FGT68L9p34MLZWK65wq_
36
36
  airtrain/integrations/cerebras/skills.py,sha256=BJEb_7TglCYAukD3kcx37R8ibnJWdxVrBrwf3ZTYP-4,4924
37
37
  airtrain/integrations/combined/__init__.py,sha256=EL_uZs8436abeZA6NbM-gLdur7kJr4YfGGOYybKOhjc,467
38
38
  airtrain/integrations/combined/groq_fireworks_skills.py,sha256=Kz8UDU4-Rl71znz3ml9qVMRz669iWx4sUl37iafW0NU,4612
39
- airtrain/integrations/combined/list_models_factory.py,sha256=aLYEpJdbWVmvrrxa4e3_N0Lqq4_ZOFkByWKIqFCmJfI,7195
39
+ airtrain/integrations/combined/list_models_factory.py,sha256=pFvIMeAZSRC7e51qknD9LzLkNhNgZL4pItzGNGzZdNY,8263
40
40
  airtrain/integrations/fireworks/__init__.py,sha256=GstUg0rYC-7Pg0DVbDXwL5eO1hp3WCSfroWazbGpfi0,545
41
41
  airtrain/integrations/fireworks/completion_skills.py,sha256=zxx7aNlum9scQMri5Ek0qN8VfAomhyUp3u8JJo_AFWM,5615
42
42
  airtrain/integrations/fireworks/conversation_manager.py,sha256=ifscKHYKWM_NDElin-oTzpRhyoh6pzBnklmMuH5geOY,3706
@@ -64,9 +64,19 @@ airtrain/integrations/openai/credentials.py,sha256=NfRyp1QgEtgm8cxt2-BOLq-6d0X-P
64
64
  airtrain/integrations/openai/list_models.py,sha256=vg8pZwLZ3F2Fx42X18WykpJOzZD9JG-2KJi49XWgSKo,4121
65
65
  airtrain/integrations/openai/models_config.py,sha256=W9mu_z9tCC4ZUKHSJ6Hk4X09TRZLqEhT7TtRY5JEk5g,8007
66
66
  airtrain/integrations/openai/skills.py,sha256=kEDe5q0Zlv_X-JGOYtb552ktb3aQQYVUYczVwMH0jxA,12823
67
+ airtrain/integrations/perplexity/__init__.py,sha256=asVQs-oVXVhnLmAOZ6l9R8KoigeHmw8D5OONb_aGgnY,1189
68
+ airtrain/integrations/perplexity/credentials.py,sha256=5acl2DcF0dk7DQJWRwmFkBXPQ1zfKN4ARW31dP-4rpQ,1524
69
+ airtrain/integrations/perplexity/list_models.py,sha256=iOOxQ50BfLc226ps0gSdFl-enxa-SmStiuU2vFTUhJ0,4298
70
+ airtrain/integrations/perplexity/models_config.py,sha256=cMHk6kjy-u8KfxU0KmuJ9rLiquwSGuj_-SOtj9wHlM4,4060
71
+ airtrain/integrations/perplexity/skills.py,sha256=-RBwnv5wg0s-TwG--ZrCy2WikuMpNx15FGdsoNctRB0,10545
67
72
  airtrain/integrations/sambanova/__init__.py,sha256=dp_263iOckM_J9pOEvyqpf3FrejD6-_x33r0edMCTe0,179
68
73
  airtrain/integrations/sambanova/credentials.py,sha256=JyN8sbMCoXuXAjim46aI3LTicBijoemS7Ao0rn4yBJU,824
69
74
  airtrain/integrations/sambanova/skills.py,sha256=SZ_GAimMiOCILiNkzyhNflyRR6bdC5r0Tnog19K8geU,4997
75
+ airtrain/integrations/search/__init__.py,sha256=r-hCYff5IdgI4IgQyMUzKzc_CVryl1oiweaGC3TwZ-8,405
76
+ airtrain/integrations/search/exa/__init__.py,sha256=YdINIFcuT4pC72obDYx7DHVdMzlEg03BRQJi57SKc3g,486
77
+ airtrain/integrations/search/exa/credentials.py,sha256=yOJIQdWs6RXX2Msk453VHH7FYQffpF946afaEWgVg4o,900
78
+ airtrain/integrations/search/exa/schemas.py,sha256=QsTh-UfCAJ-YCy0TgjC7iJDADBCdY3Eu4NBi9BSGiuk,3791
79
+ airtrain/integrations/search/exa/skills.py,sha256=zCoSOxeJk27umLHdYVZbW5sDz-kcbuBzH0SE0C7lLZg,3754
70
80
  airtrain/integrations/together/__init__.py,sha256=5p2v5kvryl-Z8eSMxdrMQtgzynPo6zQ6vZqpxBADA1I,878
71
81
  airtrain/integrations/together/audio_models_config.py,sha256=GtqfmKR1vJ5x4B3kScvEO3x4exvzwNP78vcGVTk_fBE,1004
72
82
  airtrain/integrations/together/credentials.py,sha256=cYNhyIwgsxm8LfiFfT-omBvgV3mUP6SZeRSukyzzDlI,747
@@ -84,13 +94,15 @@ airtrain/integrations/together/vision_models_config.py,sha256=m28HwYDk2Kup_J-a1F
84
94
  airtrain/telemetry/__init__.py,sha256=_xDHzSmQvRCqihT0QTmF7bS9yKEl2LaZ3Zq05hXaI3k,1108
85
95
  airtrain/telemetry/service.py,sha256=6IB2-FL3fGsYYgMIVQ9MNCp9UlSE5cVhLlB3VBYkAnY,5592
86
96
  airtrain/telemetry/views.py,sha256=qob1swyLNEk_UIpDi5VTwMDsEsGZhJheFQrGbP8T5hw,8115
87
- airtrain/tools/__init__.py,sha256=RRqbnUqKorql5D1FDQArxpdwC5d8kUaeNzN9iZKY8V8,827
88
- airtrain/tools/command.py,sha256=-Qmto_PqivHQBasW8PtSYoTk_NR5Wl4zz4n7u71egMQ,6983
97
+ airtrain/tools/__init__.py,sha256=AauO_EEBcK09mkyCuvvVK_Oz0L5DOrnuySViWXCOt6Y,1021
98
+ airtrain/tools/command.py,sha256=dxvs6RzppjWmkUe1oMtxOc7w2mFOGFFZ9Gylwnm37Sw,13355
89
99
  airtrain/tools/filesystem.py,sha256=-YYdHj_KeSWPYXeRhWhIX9s_KujVA1R5tF3r93zRVTU,6324
90
100
  airtrain/tools/network.py,sha256=YR0AtMXDXkhCsXcx7_t2d12ItnKY8XXTmyP1kdj2M4c,3883
91
101
  airtrain/tools/registry.py,sha256=K-1H5EipYcDNDx2jdpsEY9gjfV4aNCGI1pY2UsgSpC0,10246
92
- airtrain-0.1.58.dist-info/METADATA,sha256=3FCm8ujh-Lu1bEttg5i2GhsONwI5WwjQYAhSl2R10P0,6503
93
- airtrain-0.1.58.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
94
- airtrain-0.1.58.dist-info/entry_points.txt,sha256=rrJ36IUsyq6n1dSfTWXqVAgpQLPRWDfCqwd6_3B-G0U,52
95
- airtrain-0.1.58.dist-info/top_level.txt,sha256=cFWW1vY6VMCb3AGVdz6jBDpZ65xxBRSqlsPyySxTkxY,9
96
- airtrain-0.1.58.dist-info/RECORD,,
102
+ airtrain/tools/search.py,sha256=MJNi17g6aBPSqbF0ChV8ZgMlzz_PoKSPAIpe_dazdt8,15081
103
+ airtrain/tools/testing.py,sha256=q4ALEPRzukiadY6wFSPY7vA-T1o3XInLhXt18dsf6yY,4397
104
+ airtrain-0.1.62.dist-info/METADATA,sha256=fAOQQht25N594JsKGN6sFuiqjVMPjKMtly_RP6JW9co,6503
105
+ airtrain-0.1.62.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
106
+ airtrain-0.1.62.dist-info/entry_points.txt,sha256=rrJ36IUsyq6n1dSfTWXqVAgpQLPRWDfCqwd6_3B-G0U,52
107
+ airtrain-0.1.62.dist-info/top_level.txt,sha256=cFWW1vY6VMCb3AGVdz6jBDpZ65xxBRSqlsPyySxTkxY,9
108
+ airtrain-0.1.62.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (77.0.3)
2
+ Generator: setuptools (78.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5