Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
726 changes: 264 additions & 462 deletions docs/examples/advanced-usage.md

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add an example using the get_all_results_async method showing how to async fetch all results (without them having to pagniate)?

I think this would be more helpful than having multiple pagination examples.

Large diffs are not rendered by default.

843 changes: 100 additions & 743 deletions docs/examples/creating-evaluations.md

Large diffs are not rendered by default.

1,171 changes: 185 additions & 986 deletions docs/examples/retrieving-results.md

Large diffs are not rendered by default.

760 changes: 138 additions & 622 deletions docs/examples/timeouts.md

Large diffs are not rendered by default.

44 changes: 44 additions & 0 deletions examples/all_results_no_pagination.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#!/usr/bin/env -S poetry run python

import asyncio

from atlas import AsyncAtlas


async def main():
# Construct async client
client = AsyncAtlas()

# --- Models
models = await client.models.get()
print(f"Found {len(models)} models")

# --- Benchmarks
benchmarks = await client.benchmarks.get()
print(f"Found {len(benchmarks)} benchmarks")

# --- Create evaluation
evaluation = await client.evaluations.create(
model=models[0],
benchmark=benchmarks[0],
)
print(f"Created evaluation {evaluation.id}, status={evaluation.status}")

# --- Wait for completion
evaluation = await client.evaluations.wait_for_completion(
evaluation,
interval_seconds=10,
# Keep in mind that the evaluation will take a while to complete, so you may want to increase the timeout
# or grab the evaluation id and check the status later
timeout_seconds=600, # 10 minutes
)
print(f"Evaluation {evaluation.id} finished with status={evaluation.status}")

# --- All results at once without pagination
results = await client.results.get_all(evaluation=evaluation)
print(f"Found {len(results)} results")
print(results)


if __name__ == "__main__":
asyncio.run(main())
2 changes: 1 addition & 1 deletion examples/async_client_simple.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ async def main():
print(f"Created evaluation {evaluation.id}, status={evaluation.status}")

# --- Wait for completion
await evaluation.wait_for_completion_async(interval_seconds=10, timeout=600)
await evaluation.wait_for_completion_async(interval_seconds=10, timeout_seconds=600)
print(f"Evaluation {evaluation.id} finished with status={evaluation.status}")

# --- Results
Expand Down
25 changes: 25 additions & 0 deletions examples/get_benchmarks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/usr/bin/env -S poetry run python

import asyncio

from atlas import AsyncAtlas


async def main():
# Construct async client
client = AsyncAtlas()

# --- Get benchmarks by name
benchmark_name = "mmlu"
benchmarks = await client.benchmarks.get(name=benchmark_name)
print(f"Found {len(benchmarks)} benchmarks with name {benchmark_name}")
print(benchmarks)

# --- Get benchmarks by type
benchmark_type = "public"
benchmarks = await client.benchmarks.get(type=benchmark_type)
print(f"Found {len(benchmarks)} benchmarks with type {benchmark_type}")
print(benchmarks)

if __name__ == "__main__":
asyncio.run(main())
19 changes: 19 additions & 0 deletions examples/get_evaluation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/usr/bin/env -S poetry run python

import asyncio

from atlas import AsyncAtlas


async def main():
# Construct async client
client = AsyncAtlas()

# --- Get evaluation by id
evaluation_id = "eval_123"
evaluation = await client.evaluations.get(evaluation_id)
print(f"Found evaluation {evaluation.id}")
print(evaluation)

if __name__ == "__main__":
asyncio.run(main())
37 changes: 37 additions & 0 deletions examples/get_models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#!/usr/bin/env -S poetry run python

import asyncio

from atlas import AsyncAtlas


async def main():
# Construct async client
client = AsyncAtlas()

# --- Get models by name
model_name = "gpt-4o"
models = await client.models.get(name=model_name)
print(f"Found {len(models)} models with name {model_name}")
print(models)

# --- Get models by company
company_names = ["openai", "anthropic"]
models = await client.models.get(companies=company_names)
print(f"Found {len(models)} models with companies {company_names}")
print(models)

# --- Get models by region
region_names = ["usa"]
models = await client.models.get(regions=region_names)
print(f"Found {len(models)} models with regions {region_names}")
print(models)

# --- Get models by type
model_type = "public"
models = await client.models.get(type=model_type)
print(f"Found {len(models)} models with type {model_type}")
print(models)

if __name__ == "__main__":
asyncio.run(main())
105 changes: 105 additions & 0 deletions examples/paginated_results.py

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we have another example get_results that uses the get_all_results_async to do the same thing?

To show users they don't have to handle pagination for results themselves if they just want to fetch all results.

Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#!/usr/bin/env -S poetry run python

import asyncio

from atlas import AsyncAtlas


async def main():
# Construct async client
client = AsyncAtlas()

# --- Models
models = await client.models.get()
print(f"Found {len(models)} models")

# --- Benchmarks
benchmarks = await client.benchmarks.get()
print(f"Found {len(benchmarks)} benchmarks")

# --- Create evaluation
evaluation = await client.evaluations.create(
model=models[0],
benchmark=benchmarks[0],
)
print(f"Created evaluation {evaluation.id}, status={evaluation.status}")

# --- Wait for completion
evaluation = await client.evaluations.wait_for_completion(
evaluation,
interval_seconds=10,
# Keep in mind that the evaluation will take a while to complete, so you may want to increase the timeout
# or grab the evaluation id and check the status later
timeout_seconds=600, # 10 minutes
)
print(f"Evaluation {evaluation.id} finished with status={evaluation.status}")

# --- Results with pagination
if evaluation.is_success:
print("Fetching all results with pagination...")

all_results = []
page = 1
page_size = 50

while True:
print(f"Fetching page {page} (page size: {page_size})...")

# Get results for current page
results_data = await client.results.get_by_id(
evaluation_id=evaluation.id,
page=page,
page_size=page_size
)

if not results_data or not results_data.results:
print("No more results to fetch")
break

# Add current page results to our collection
all_results.extend(results_data.results)

# Show progress
if page == 1:
total_count = results_data.pagination.total_count
total_pages = results_data.pagination.total_pages
print(f"Total results: {total_count:,}")
print(f"Total pages: {total_pages}")

print(f"Page {page}: Retrieved {len(results_data.results)} results")
print(f"Running total: {len(all_results):,} results")

# Check if we've reached the last page
if page >= results_data.pagination.total_pages:
print("Reached last page")
break

page += 1

# Summary of all results
print(f"\n=== PAGINATION COMPLETE ===")
print(f"Total results collected: {len(all_results):,}")

if all_results:
# Calculate some basic statistics
correct_answers = sum(1 for r in all_results if r.score > 0.5)
accuracy = correct_answers / len(all_results)
avg_score = sum(r.score for r in all_results) / len(all_results)

print(f"Overall accuracy: {accuracy:.1%} ({correct_answers:,}/{len(all_results):,})")
print(f"Average score: {avg_score:.3f}")

# Show a few example results
print(f"\nFirst 3 results:")
for i, result in enumerate(all_results[:3], 1):
print(f" {i}. Score: {result.score:.3f}, Subset: {result.subset}")
print(f" Prompt: {result.prompt[:100]}...")
print(f" Response: {result.result[:100]}...")
print()

else:
print("Evaluation did not succeed, no results to show.")


if __name__ == "__main__":
asyncio.run(main())
8 changes: 4 additions & 4 deletions src/atlas/resources/results/results.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

DEFAULT_PAGE = 1
DEFAULT_PAGE_SIZE = 100
MAX_PAGE_SIZE = 500


class Results(SyncAPIResource):
Expand Down Expand Up @@ -68,12 +69,11 @@ def get_by_id(
"""
params = {"evaluation_id": evaluation_id}

effective_page_size = page_size if page_size is not None else DEFAULT_PAGE_SIZE
effective_page_size = min(max(page_size, 1), MAX_PAGE_SIZE) if page_size is not None else DEFAULT_PAGE_SIZE
effective_page = page if page is not None else DEFAULT_PAGE

params["page"] = str(effective_page)
if page_size is not None:
params["pageSize"] = str(page_size)
params["pageSize"] = str(effective_page_size)

# Get the response with cast_to to get parsed data
resp = self._get(
Expand Down Expand Up @@ -221,7 +221,7 @@ async def get_by_id(
"""
params = {"evaluation_id": evaluation_id}

effective_page_size = page_size if page_size is not None else DEFAULT_PAGE_SIZE
effective_page_size = min(max(page_size, 1), MAX_PAGE_SIZE) if page_size is not None else DEFAULT_PAGE_SIZE
effective_page = page if page is not None else DEFAULT_PAGE

params["page"] = str(effective_page)
Expand Down
16 changes: 8 additions & 8 deletions tests/resources/test_results.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ def test_get_results_request_parameters(self, results_resource, mock_results_res

results_resource._get.assert_called_once_with(
"/results",
params={"evaluation_id": "eval-456", "page": "1"},
params={"evaluation_id": "eval-456", "page": "1", "pageSize": "100"},
timeout=DEFAULT_TIMEOUT,
cast_to=dict,
)
Expand Down Expand Up @@ -642,7 +642,7 @@ def test_get_results_default_page_parameter(self, results_resource, sample_resul
call_args = results_resource._get.call_args
params = call_args.kwargs["params"]
assert params["page"] == "1"
assert "pageSize" not in params # pageSize should not be included when not specified
assert params["pageSize"] == "100" # pageSize is now always included with default value

def test_get_results_pagination_metadata_calculation(self, results_resource, sample_result_data):
"""get method correctly calculates pagination metadata."""
Expand Down Expand Up @@ -829,8 +829,8 @@ def test_get_results_zero_page_size_edge_case(self, results_resource):
result = results_resource.get_by_id(evaluation_id="eval-123", page_size=0)

assert isinstance(result, ResultsResponse)
# Should use 0 as provided (though this might cause division by zero, it's handled)
assert result.pagination.page_size == 0
# page_size of 0 should be corrected to minimum value of 1
assert result.pagination.page_size == 1

def test_get_results_negative_page_values(self, results_resource):
"""get method handles negative page values."""
Expand All @@ -854,9 +854,9 @@ def test_get_results_negative_page_values(self, results_resource):
call_args = results_resource._get.call_args
params = call_args.kwargs["params"]
assert params["page"] == "-1"
assert params["pageSize"] == "-50"
assert params["pageSize"] == "1" # negative page_size should be corrected to 1

assert isinstance(result, ResultsResponse)
assert result.pagination.page_size == -50
# total_pages calculation with negative page_size
assert result.pagination.total_pages == 0 # math.ceil handles negative divisors
assert result.pagination.page_size == 1 # negative page_size should be corrected to 1
# total_pages calculation with corrected page_size
assert result.pagination.total_pages == 100 # math.ceil(100/1) = 100
Loading