Skip to content

Commit 4e23680

Browse files
committed
Merge branch 'main' into create_custom_tools
2 parents 025f6a6 + c82a30f commit 4e23680

File tree

7 files changed

+172
-60
lines changed

7 files changed

+172
-60
lines changed

agentstack/_tools/py_sql/config.json

Lines changed: 0 additions & 37 deletions
This file was deleted.

agentstack/_tools/py_sql/__init__.py renamed to agentstack/_tools/sql/__init__.py

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,30 @@
22
import psycopg2
33
from typing import Dict, Any
44

5-
def get_connection():
5+
connection = None
6+
7+
def _get_connection():
68
"""Get PostgreSQL database connection"""
7-
return psycopg2.connect(
8-
dbname=os.getenv('POSTGRES_DB'),
9-
user=os.getenv('POSTGRES_USER'),
10-
password=os.getenv('POSTGRES_PASSWORD'),
11-
host=os.getenv('POSTGRES_HOST', 'localhost'),
12-
port=os.getenv('POSTGRES_PORT', '5432')
13-
)
9+
10+
global connection
11+
if connection is None:
12+
connection = psycopg2.connect(
13+
dbname=os.getenv('POSTGRES_DB'),
14+
user=os.getenv('POSTGRES_USER'),
15+
password=os.getenv('POSTGRES_PASSWORD'),
16+
host=os.getenv('POSTGRES_HOST', 'localhost'),
17+
port=os.getenv('POSTGRES_PORT', '5432')
18+
)
19+
20+
return connection
1421

1522
def get_schema() -> Dict[str, Any]:
1623
"""
1724
Initialize connection and get database schema.
1825
Returns a dictionary containing the database schema.
1926
"""
2027
try:
21-
conn = get_connection()
28+
conn = _get_connection()
2229
cursor = conn.cursor()
2330

2431
# Query to get all tables in the current schema
@@ -47,7 +54,7 @@ def get_schema() -> Dict[str, Any]:
4754
schema[table_name] = columns
4855

4956
cursor.close()
50-
conn.close()
57+
# conn.close()
5158
return schema
5259

5360
except Exception as e:
@@ -63,15 +70,15 @@ def execute_query(query: str) -> list:
6370
List of query results
6471
"""
6572
try:
66-
conn = get_connection()
73+
conn = _get_connection()
6774
cursor = conn.cursor()
6875

6976
# Execute the query
7077
cursor.execute(query)
7178
results = cursor.fetchall()
7279

7380
cursor.close()
74-
conn.close()
81+
# conn.close()
7582
return results
7683

7784
except Exception as e:

agentstack/_tools/sql/config.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"name": "sql",
3+
"url": "https://pypi.org/project/psycopg2/",
4+
"category": "database",
5+
"env": {
6+
"POSTGRES_DB": null,
7+
"POSTGRES_USER": null,
8+
"POSTGRES_PASSWORD": null,
9+
"POSTGRES_HOST": null,
10+
"POSTGRES_PORT": null
11+
},
12+
"dependencies": [
13+
"psycopg2-binary>=2.9.9"
14+
],
15+
"tools": [
16+
"get_schema",
17+
"execute_query"
18+
],
19+
"cta": "Set up your PostgreSQL connection variables in the environment file."
20+
}

docs/compile_llms_txt.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,26 @@
11
import os
2+
from pathlib import Path
23

34
def compile_llms_txt():
4-
# Get the docs directory path (where this script is located)
5-
docs_dir = os.path.dirname(os.path.abspath(__file__))
5+
# Get the current working directory
6+
current_dir = Path(os.getcwd())
67
content = ''
8+
79
# Define names of directories and files to exclude
810
excluded_names = {'tool'}
911

10-
# Change to docs directory
11-
os.chdir(docs_dir)
12-
1312
for root, _, files in os.walk('.'):
1413
# Get the last part of the current directory
1514
current_dir = os.path.basename(root)
1615
if current_dir in excluded_names:
1716
continue
1817

1918
for file in files:
19+
# Check if the file is an MDX file and not in excluded names
2020
if file.endswith('.mdx'):
21-
if file in excluded_names:
21+
# Extract the base name without extension for exclusion check
22+
base_name = os.path.splitext(file)[0]
23+
if base_name in excluded_names:
2224
continue
2325

2426
file_path = os.path.join(root, file)
@@ -28,10 +30,9 @@ def compile_llms_txt():
2830
file_content = f.read()
2931
content += f"## {relative_path}\n\n{file_content}\n\n"
3032

31-
# Write the complete content, replacing the existing file
32-
output_path = os.path.join(docs_dir, 'llms.txt')
33-
with open(output_path, 'w', encoding='utf-8') as f:
34-
f.write(content)
33+
# Write the complete content to llms.txt in the current directory
34+
output_path = Path('llms.txt')
35+
output_path.write_text(content, encoding='utf-8')
3536

3637
if __name__ == "__main__":
3738
compile_llms_txt()

docs/tools/core.mdx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,12 @@ description: 'AgentStack tools that are not third-party integrations'
1313

1414
- [Code Interpreter](/tools/tool/code-interpreter)
1515

16-
## Data Input
16+
## Input
1717
- [Vision](/tools/tool/vision)
1818

19+
## Data
20+
- [SQL](/tools/tool/sql)
21+
1922
<CardGroup cols={1}>
2023
<Card
2124
title="Community Tools"

docs/tools/tool/sql.mdx

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
---
2+
title: 'Perplexity'
3+
description: 'Agentic Search'
4+
---
5+
6+
## Description
7+
Enable your agent to perform queries directly on a database
8+
9+
<Note>
10+
There is no built-in sandboxing using this tool. Agents may perform destructive queries that may be irreversible.
11+
</Note>
12+
13+
## Installation
14+
15+
```bash
16+
agentstack tools add sql
17+
```
18+
19+
Set the API keys
20+
```env
21+
POSTGRES_DB=...
22+
POSTGRES_USER=...
23+
POSTGRES_PASSWORD=...
24+
POSTGRES_HOST=...
25+
POSTGRES_PORT=...
26+
```

tests/test_compile_llms.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import os
2+
import shutil
3+
import tempfile
4+
import unittest
5+
from pathlib import Path
6+
7+
import sys
8+
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
9+
from docs.compile_llms_txt import compile_llms_txt
10+
11+
class TestCompileLLMsTxt(unittest.TestCase):
12+
def setUp(self):
13+
self.original_cwd = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
14+
15+
# Create a temporary directory for test files
16+
self.test_dir = tempfile.mkdtemp()
17+
self.docs_dir = Path(self.test_dir)
18+
19+
# Change to the temporary directory
20+
os.chdir(self.docs_dir)
21+
22+
def tearDown(self):
23+
os.chdir(self.original_cwd)
24+
shutil.rmtree(self.test_dir)
25+
26+
def create_test_mdx_file(self, path: str, content: str):
27+
"""Helper to create test MDX files"""
28+
file_path = self.docs_dir / path
29+
file_path.parent.mkdir(parents=True, exist_ok=True)
30+
file_path.write_text(content)
31+
32+
def test_basic_compilation(self):
33+
"""Test basic MDX file compilation"""
34+
# Create test MDX files
35+
self.create_test_mdx_file("test1.mdx", "Test content 1")
36+
self.create_test_mdx_file("test2.mdx", "Test content 2")
37+
38+
# Run compilation
39+
compile_llms_txt()
40+
41+
# Check output file exists and contains expected content
42+
output_path = self.docs_dir / "llms.txt"
43+
self.assertTrue(output_path.exists())
44+
45+
content = output_path.read_text()
46+
self.assertIn("## test1.mdx", content)
47+
self.assertIn("Test content 1", content)
48+
self.assertIn("## test2.mdx", content)
49+
self.assertIn("Test content 2", content)
50+
51+
def test_excluded_directories(self):
52+
"""Test that files in excluded directories are skipped"""
53+
# Create files in both regular and excluded directories
54+
self.create_test_mdx_file("regular/file.mdx", "Regular content")
55+
self.create_test_mdx_file("tool/file.mdx", "Tool content")
56+
57+
compile_llms_txt()
58+
59+
content = (self.docs_dir / "llms.txt").read_text()
60+
self.assertIn("Regular content", content)
61+
self.assertNotIn("Tool content", content)
62+
63+
def test_excluded_files(self):
64+
"""Test that excluded files are skipped"""
65+
self.create_test_mdx_file("regular.mdx", "Regular content")
66+
self.create_test_mdx_file("tool.mdx", "Tool content")
67+
68+
compile_llms_txt()
69+
70+
content = (self.docs_dir / "llms.txt").read_text()
71+
self.assertIn("Regular content", content)
72+
self.assertNotIn("Tool content", content)
73+
74+
def test_nested_directories(self):
75+
"""Test compilation from nested directory structure"""
76+
self.create_test_mdx_file("dir1/test1.mdx", "Content 1")
77+
self.create_test_mdx_file("dir1/dir2/test2.mdx", "Content 2")
78+
79+
compile_llms_txt()
80+
81+
content = (self.docs_dir / "llms.txt").read_text()
82+
self.assertIn("## dir1/test1.mdx", content)
83+
self.assertIn("## dir1/dir2/test2.mdx", content)
84+
self.assertIn("Content 1", content)
85+
self.assertIn("Content 2", content)
86+
87+
def test_empty_directory(self):
88+
"""Test compilation with no MDX files"""
89+
compile_llms_txt()
90+
91+
content = (self.docs_dir / "llms.txt").read_text()
92+
self.assertEqual(content, "")

0 commit comments

Comments
 (0)