Skip to content

Commit 6c5f838

Browse files
committed
set up initial test for sync operation
1 parent ba9e223 commit 6c5f838

File tree

6 files changed

+212
-22
lines changed

6 files changed

+212
-22
lines changed

poetry.lock

Lines changed: 106 additions & 20 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
[project]
22
name = "humanloop"
3+
description = "The Humanloop Python Library"
4+
authors = []
5+
keywords = ["ai", "machine-learning", "llm", "sdk", "humanloop"]
36

47
[tool.poetry]
58
name = "humanloop"
@@ -54,7 +57,7 @@ pydantic = ">= 1.9.2"
5457
pydantic-core = "^2.18.2"
5558
typing_extensions = ">= 4.0.0"
5659

57-
[tool.poetry.dev-dependencies]
60+
[tool.poetry.group.dev.dependencies]
5861
mypy = "1.0.1"
5962
pytest = "^7.4.0"
6063
pytest-asyncio = "^0.23.5"

src/humanloop/client.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,9 @@ def agent():
347347
path=path,
348348
attributes=attributes,
349349
)
350+
351+
def sync(self):
352+
return "Hello world"
350353

351354

352355
class AsyncHumanloop(AsyncBaseHumanloop):

src/humanloop/sync/__init__.py

Whitespace-only changes.

tests/conftest.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,10 @@ def api_keys() -> APIKeys:
192192

193193
@pytest.fixture(scope="session")
194194
def humanloop_client(api_keys: APIKeys) -> Humanloop:
195-
return Humanloop(api_key=api_keys.humanloop)
195+
return Humanloop(
196+
api_key=api_keys.humanloop,
197+
base_url="http://localhost:80/v5",
198+
)
196199

197200

198201
@pytest.fixture(scope="session", autouse=True)

tests/sync/test_sync.py

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import pytest
2+
from humanloop import Humanloop, FileType
3+
from pathlib import Path
4+
from typing import List, NamedTuple
5+
6+
7+
class SyncableFile(NamedTuple):
8+
path: str
9+
type: FileType
10+
model: str
11+
id: str = ""
12+
13+
14+
@pytest.fixture
15+
def test_file_structure(humanloop_client: Humanloop, get_test_path) -> List[SyncableFile]:
16+
"""Creates a predefined structure of files in Humanloop for testing sync"""
17+
files: List[SyncableFile] = [
18+
SyncableFile(
19+
path="prompts/gpt-4",
20+
type="prompt",
21+
model="gpt-4",
22+
),
23+
SyncableFile(
24+
path="prompts/gpt-4o",
25+
type="prompt",
26+
model="gpt-4o",
27+
),
28+
SyncableFile(
29+
path="prompts/nested/complex/gpt-4o",
30+
type="prompt",
31+
model="gpt-4o",
32+
),
33+
SyncableFile(
34+
path="agents/gpt-4",
35+
type="agent",
36+
model="gpt-4",
37+
),
38+
SyncableFile(
39+
path="agents/gpt-4o",
40+
type="agent",
41+
model="gpt-4o",
42+
),
43+
]
44+
45+
# Create the files in Humanloop
46+
created_files = []
47+
for file in files:
48+
full_path = get_test_path(file.path)
49+
if file.type == "prompt":
50+
response = humanloop_client.prompts.upsert(
51+
path=full_path,
52+
model=file.model,
53+
)
54+
elif file.type == "agent":
55+
response = humanloop_client.agents.upsert(
56+
path=full_path,
57+
model=file.model,
58+
)
59+
created_files.append(SyncableFile(path=full_path, type=file.type, model=file.model, id=response.id))
60+
61+
return created_files
62+
63+
64+
@pytest.fixture
65+
def cleanup_local_files():
66+
"""Cleanup any locally synced files after tests"""
67+
yield
68+
# Clean up the local humanloop directory after tests
69+
local_dir = Path("humanloop")
70+
if local_dir.exists():
71+
import shutil
72+
73+
shutil.rmtree(local_dir)
74+
75+
76+
def test_sync_basic(humanloop_client: Humanloop, test_file_structure: List[SyncableFile], cleanup_local_files):
77+
"""Test that humanloop.sync() correctly syncs remote files to local filesystem"""
78+
# Run the sync
79+
successful_files = humanloop_client.sync()
80+
81+
# Verify each file was synced correctly
82+
for file in test_file_structure:
83+
# Get the extension based on file type: .prompt, .agent
84+
extension = f".{file.type}"
85+
86+
# The local path should mirror the remote path structure
87+
local_path = Path("humanloop") / f"{file.path}{extension}"
88+
89+
# Basic assertions
90+
assert local_path.exists(), f"Expected synced file at {local_path}"
91+
assert local_path.parent.exists(), f"Expected directory at {local_path.parent}"
92+
93+
# Verify it's not empty
94+
content = local_path.read_text()
95+
assert content, f"File at {local_path} should not be empty"

0 commit comments

Comments
 (0)