Skip to contents

This file contains repository-specific guidance. Also review organization-level guidance: - AGENTS.organization.md - AGENTS.style.coding.md


Package Overview

artcurator provides:

  1. Unified AI Interface - Provider-agnostic artwork analysis (artgemini/artopenai abstraction)
  2. Curator Context - Hot-cached artist/artwork data for AI agents
  3. Benchmark Data - Percentile metrics for artistic analysis

See README.md for user-facing overview.


Testing Strategy

httptest2 Fixture System

All tests use httptest2 fixtures - no live API calls, no environment-based skips.

Pattern:

# tests/testthat/test-artwork-ai.R
httptest2::with_mock_dir(simplify = FALSE, arthelpers::path_mocks("unified/1"), {
    result <- art_about_ai(img, provider = "gemini", ml = "gemini-2.5-flash")
    expect_type(result, "character")
})

Key files: - tests/testthat/setup.R - httptest2 configuration (block_requests, redactors) - inst/httptest2/redact.R - Automatic redaction (API keys, base64 images) - tests/testthat/fixtures/_mocks/ - Recorded HTTP responses - tests/testthat/fixtures/_extra/ - Shared test assets (images, data)

Recording Fixtures

First time only:

# Set real API keys
Sys.setenv(ART_GEMINI_KEY = "your-key", ART_OPENAI_KEY = "sk-your-key")

# Record fixtures
httptest2::capture_requests({ devtools::test(filter = "artwork-ai") })

# Commit fixtures
git add tests/testthat/fixtures/_mocks/
git commit -m "Record httptest2 test fixtures"

Subsequent runs: Tests use fixtures automatically (zero API calls).

Refreshing fixtures: Delete tests/testthat/fixtures/_mocks/unified/ and re-record.

Helper Functions

Use arthelpers (in Suggests) for test utilities: - arthelpers::path_mocks("subdir") - Mock fixture directory - arthelpers::path_extra("file") - Extra fixtures directory - arthelpers::dev_artist() - Test artist UUID - arthelpers::dev_artwork() - Test artwork UUID

Never use these in R/* files - tests/vignettes only.


Vignette Building

httptest2 + Quarto Freeze

Vignettes use combined httptest2 mocking and Quarto freeze caching for zero-cost CI builds.

Every vignette has:

start_vignette("vignette-name")  # Top
# ... executable code ...
end_vignette()                    # Bottom

Plus YAML: eval: true, freeze: auto

Recording vignette fixtures (first time only):

export ART_GEMINI_KEY="your-key"
export ART_OPENAI_KEY="sk-your-key"

rm -rf vignettes/*/ _freeze/  # Clean slate
quarto render vignettes/      # Records fixtures + cache

git add vignettes/*/ _freeze/
git commit -m "Record vignette fixtures and freeze cache"

CI builds: Uses _freeze/ cache (no API calls, no compute) = $0

Important files: - _quarto.yml - Project-level freeze config - inst/httptest2/redact.R - Redactor (auto-used) - vignettes/{name}/ - httptest2 fixtures (commit to git) - _freeze/ - Quarto cache (commit to git)


Architecture

Unified Interface Pattern

artcurator::art_about_ai(img, provider = "gemini")
    ↓
.dispatch_artwork_ai("art_about_ai", "gemini", img_path = img)
    ↓
do.call(artgemini::art_about_ai, list(img_path = img))

Provider resolution: parameter → env var → auto-detect → error

Dependencies

Both artgemini and artopenai are in Imports (not Suggests) to guarantee functionality:

  • Out-of-box experience: All art_*_ai() functions work immediately
  • No provider confusion: Users don’t need to research which provider to install
  • Complete feature access: Users can compare providers, use both in workflows
  • Simplified installation: Single pak::pkg_install("artalytics/artcurator") gets both

Trade-off: Larger dependency footprint, but ensures unified interface works as advertised.

Both use conditional loading (requireNamespace()) internally for clean error handling.


Common Tasks

Add New Unified Function

  1. Wrapper in R/artwork-ai.R using .dispatch_artwork_ai()
  2. Roxygen docs with @export
  3. Test with httptest2 fixtures
  4. Update NEWS.md

Update Prompts

  1. Edit inst/prompts/*.txt
  2. Increment PROMPT_VERSION if semantic change
  3. Re-record affected fixtures
  4. Commit

Refresh Fixtures

rm -rf tests/testthat/fixtures/_mocks/unified/
httptest2::capture_requests({ devtools::test(filter = "artwork-ai") })
git add tests/testthat/fixtures/ && git commit -m "Refresh test fixtures"

Troubleshooting

Issue Solution
“Expected mock file” Record fixtures (see above)
Vignettes making live API calls Record vignette fixtures or git pull
“Namespaces in Imports not imported” Expected - conditional requireNamespace()
Vignette re-executes every time Commit _freeze/ to git

See Also