Nothing
# Tests for Gemini Provider
library(testthat)
library(aisdk)
# Load helper functions (for environment variable handling)
helper_path <- file.path(test_path("helper-env.R"))
if (file.exists(helper_path)) {
source(helper_path)
}
# Provide defaults if helper-env doesn't define them
if (!exists("get_gemini_model")) {
get_gemini_model <- function() "gemini-2.5-flash"
}
if (!exists("get_gemini_base_url")) {
get_gemini_base_url <- function() "https://generativelanguage.googleapis.com/v1beta/models"
}
if (!exists("safe_create_provider")) {
safe_create_provider <- function(constructor, ...) {
res <- tryCatch(constructor(...), warning = function(w) constructor(...))
if (!inherits(res, "R6")) res <- constructor(...)
res
}
}
if (!exists("skip_if_no_api_key")) {
skip_if_no_api_key <- function(provider_name) {
env_var <- paste0(toupper(provider_name), "_API_KEY")
key <- Sys.getenv(env_var)
if (nchar(key) == 0) {
skip(paste(provider_name, "API key not found, skipping tests."))
}
}
}
gemini_model <- get_gemini_model()
gemini_base_url <- get_gemini_base_url()
test_that("create_gemini() creates a provider with correct defaults", {
# Use safe provider creation
provider <- safe_create_provider(create_gemini, api_key = "FAKE")
expect_s3_class(provider, "GeminiProvider")
expect_equal(provider$specification_version, "v1")
})
test_that("Gemini provider creates language model correctly", {
provider <- safe_create_provider(create_gemini, api_key = "FAKE")
model <- provider$language_model(gemini_model)
expect_s3_class(model, "GeminiLanguageModel")
expect_equal(model$model_id, gemini_model)
expect_equal(model$provider, "gemini")
expect_equal(model$specification_version, "v1")
})
test_that("Gemini provider creates image model correctly", {
provider <- safe_create_provider(create_gemini, api_key = "FAKE")
model <- provider$image_model("gemini-2.5-flash-image")
expect_s3_class(model, "GeminiImageModel")
expect_equal(model$model_id, "gemini-2.5-flash-image")
expect_equal(model$provider, "gemini")
})
test_that("create_gemini() accepts custom base_url", {
provider <- safe_create_provider(create_gemini,
api_key = "FAKE",
base_url = gemini_base_url
)
model <- provider$language_model(gemini_model)
# Model should be created successfully
expect_s3_class(model, "GeminiLanguageModel")
})
test_that("Gemini provider stores multiple base URLs for failover", {
provider <- safe_create_provider(create_gemini,
api_key = "FAKE",
base_url = "https://primary.example/v1beta/models, https://backup.example/v1beta"
)
model <- provider$language_model(gemini_model)
config <- model$get_config()
expect_equal(config$base_url, "https://primary.example/v1beta/models")
expect_equal(config$base_urls, c(
"https://primary.example/v1beta/models",
"https://backup.example/v1beta"
))
})
test_that("create_gemini() warns when API key is missing", {
# Temporarily unset API key
old_key <- Sys.getenv("GEMINI_API_KEY")
Sys.setenv(GEMINI_API_KEY = "")
on.exit(Sys.setenv(GEMINI_API_KEY = old_key))
expect_warning(
create_gemini(),
"Gemini API key not set"
)
})
# Offline Payload Verification
test_that("Gemini payload builder forms correctly", {
provider <- safe_create_provider(create_gemini, api_key = "FAKE")
model <- provider$language_model(gemini_model)
payload <- model$build_payload_internal(list(
messages = list(
list(role = "system", content = "You are an assistant."),
list(role = "user", content = "Hello!")
),
temperature = 0.5,
max_tokens = 100
), stream = FALSE)
body <- payload$body
expect_true(!is.null(body$contents))
expect_equal(body$contents[[1]]$role, "user")
expect_equal(body$contents[[1]]$parts[[1]]$text, "Hello!")
expect_true(!is.null(body$systemInstruction))
expect_equal(body$systemInstruction$parts[[1]]$text, "You are an assistant.")
expect_true(!is.null(body$generationConfig))
expect_equal(body$generationConfig$temperature, 0.5)
expect_equal(body$generationConfig$maxOutputTokens, 100)
})
test_that("Gemini payload builder emits candidate URLs for failover", {
provider <- safe_create_provider(create_gemini,
api_key = "FAKE",
base_url = "https://primary.example/v1beta/models, https://backup.example/v1beta"
)
model <- provider$language_model("gemini-test")
payload <- model$build_payload_internal(list(
messages = list(list(role = "user", content = "Hello!"))
), stream = FALSE)
expect_equal(payload$url, c(
"https://primary.example/v1beta/models/gemini-test:generateContent?key=FAKE",
"https://backup.example/v1beta/models/gemini-test:generateContent?key=FAKE"
))
})
test_that("Gemini payload builder translates multimodal content blocks", {
provider <- safe_create_provider(create_gemini, api_key = "FAKE")
model <- provider$language_model(gemini_model)
payload <- model$build_payload_internal(list(
messages = list(
list(
role = "user",
content = list(
list(type = "input_text", text = "Describe this image"),
list(
type = "input_image",
source = list(kind = "data_uri"),
data_uri = paste0(
"data:image/png;base64,",
base64enc::base64encode(charToRaw("fake-image"))
),
media_type = "image/png",
detail = "high"
)
)
)
)
), stream = FALSE)
parts <- payload$body$contents[[1]]$parts
expect_equal(parts[[1]]$text, "Describe this image")
expect_equal(parts[[2]]$inlineData$mimeType, "image/png")
expect_true(nzchar(parts[[2]]$inlineData$data))
})
test_that("Gemini payload builder passes image generation config through generationConfig", {
provider <- safe_create_provider(create_gemini, api_key = "FAKE")
model <- provider$language_model(gemini_model)
payload <- model$build_payload_internal(list(
messages = list(
list(role = "user", content = "Generate an image")
),
response_modalities = c("TEXT", "IMAGE"),
image_config = list(aspectRatio = "1:1")
), stream = FALSE)
expect_equal(payload$body$generationConfig$responseModalities, c("TEXT", "IMAGE"))
expect_equal(payload$body$generationConfig$imageConfig$aspectRatio, "1:1")
})
test_that("Gemini payload builder handles custom tool objects like google_search", {
provider <- safe_create_provider(create_gemini, api_key = "FAKE")
model <- provider$language_model(gemini_model)
payload <- model$build_payload_internal(list(
messages = list(
list(role = "user", content = "Hello!")
),
tools = list(
list(google_search = list())
)
), stream = FALSE)
body <- payload$body
expect_true(!is.null(body$tools))
expect_equal(length(body$tools), 1)
expect_true("google_search" %in% names(body$tools[[1]]))
})
test_that("Gemini payload builder handles function declarations and custom tools together", {
provider <- safe_create_provider(create_gemini, api_key = "FAKE")
model <- provider$language_model(gemini_model)
# Mock tool
mock_tool <- structure(list(
to_api_format = function(fmt) {
list(`function` = list(name = "test_func", description = "test", parameters = list(type = "object")))
}
), class = "Tool")
payload <- model$build_payload_internal(list(
messages = list(
list(role = "user", content = "Hello!")
),
tools = list(
list(google_search = list()),
mock_tool
)
), stream = FALSE)
body <- payload$body
expect_true(!is.null(body$tools))
expect_equal(length(body$tools), 2)
# Check that one is functionDeclarations and one is google_search
tool_names <- unlist(lapply(body$tools, names))
expect_true("functionDeclarations" %in% tool_names)
expect_true("google_search" %in% tool_names)
})
test_that("Gemini parser preserves inline image parts on GenerateResult", {
provider <- safe_create_provider(create_gemini, api_key = "FAKE")
model <- provider$language_model(gemini_model)
response <- list(
candidates = list(list(
finishReason = "STOP",
content = list(parts = list(
list(text = "Rendered image."),
list(inlineData = list(
mimeType = "image/png",
data = base64enc::base64encode(charToRaw("png-bytes"))
))
))
)),
usageMetadata = list(
promptTokenCount = 10,
candidatesTokenCount = 5,
totalTokenCount = 15
)
)
local_mocked_bindings(
post_to_api = function(url, headers, body, ...) response
)
result <- model$do_generate(list(
messages = list(
list(role = "user", content = "Generate an image")
)
))
expect_equal(result$text, "Rendered image.")
expect_equal(result$images[[1]]$media_type, "image/png")
expect_equal(rawToChar(result$images[[1]]$bytes), "png-bytes")
})
test_that("Gemini image model writes image artifacts", {
provider <- safe_create_provider(create_gemini, api_key = "FAKE")
model <- provider$image_model("gemini-2.5-flash-image")
response <- list(
candidates = list(list(
finishReason = "STOP",
content = list(parts = list(
list(text = "done"),
list(inlineData = list(
mimeType = "image/png",
data = base64enc::base64encode(charToRaw("png-bytes"))
))
))
)),
usageMetadata = list(
promptTokenCount = 10,
candidatesTokenCount = 5,
totalTokenCount = 15
)
)
local_mocked_bindings(
post_to_api = function(url, headers, body, ...) response
)
out_dir <- tempfile("gemini-image-out-")
result <- generate_image(
model = model,
prompt = "Generate a product photo",
output_dir = out_dir
)
expect_equal(result$text, "done")
expect_equal(result$images[[1]]$media_type, "image/png")
expect_true(file.exists(result$images[[1]]$path))
})
test_that("Gemini image model saves inline image output to disk", {
provider <- safe_create_provider(create_gemini, api_key = "FAKE")
model <- provider$image_model("gemini-2.5-flash-image")
png_bytes <- as.raw(c(137, 80, 78, 71, 13, 10, 26, 10))
response <- list(
candidates = list(list(
finishReason = "STOP",
content = list(parts = list(
list(inlineData = list(
mimeType = "image/png",
data = base64enc::base64encode(png_bytes)
)),
list(text = "done")
))
)),
usageMetadata = list(
promptTokenCount = 5,
candidatesTokenCount = 7,
totalTokenCount = 12
)
)
captured_body <- NULL
local_mocked_bindings(
post_to_api = function(url, headers, body, ...) {
captured_body <<- body
response
}
)
output_dir <- tempfile("gemini_images_")
dir.create(output_dir, recursive = TRUE)
on.exit(unlink(output_dir, recursive = TRUE), add = TRUE)
result <- model$generate_image(
prompt = "Generate a test image.",
output_dir = output_dir
)
expect_equal(captured_body$generationConfig$responseModalities[[1]], "IMAGE")
expect_equal(length(result$images), 1)
expect_true(file.exists(result$images[[1]]$path))
expect_equal(result$images[[1]]$media_type, "image/png")
expect_equal(result$text, "done")
})
test_that("Gemini image edit sends prompt plus source image and writes output", {
provider <- safe_create_provider(create_gemini, api_key = "FAKE")
model <- provider$image_model("gemini-2.5-flash-image")
png_bytes <- as.raw(c(137, 80, 78, 71, 13, 10, 26, 10))
response <- list(
candidates = list(list(
finishReason = "STOP",
content = list(parts = list(
list(inlineData = list(
mimeType = "image/png",
data = base64enc::base64encode(png_bytes)
)),
list(text = "edited")
))
))
)
captured_body <- NULL
local_mocked_bindings(
post_to_api = function(url, headers, body, ...) {
captured_body <<- body
response
}
)
output_dir <- tempfile("gemini_edit_")
dir.create(output_dir, recursive = TRUE)
on.exit(unlink(output_dir, recursive = TRUE), add = TRUE)
result <- edit_image(
model = model,
image = "https://example.com/source.png",
prompt = "Turn this into a watercolor illustration.",
output_dir = output_dir
)
parts <- captured_body$contents[[1]]$parts
expect_equal(parts[[1]]$text, "Turn this into a watercolor illustration.")
expect_equal(parts[[2]]$fileData$fileUri, "https://example.com/source.png")
expect_equal(length(result$images), 1)
expect_true(file.exists(result$images[[1]]$path))
expect_equal(result$text, "edited")
})
test_that("Gemini image edit rejects masks for now", {
provider <- safe_create_provider(create_gemini, api_key = "FAKE")
model <- provider$image_model("gemini-2.5-flash-image")
expect_error(
edit_image(
model = model,
image = "https://example.com/source.png",
prompt = "Edit this image",
mask = "https://example.com/mask.png"
),
"does not support `mask` yet"
)
})
test_that("Gemini image model edit_image sends reference image content", {
provider <- safe_create_provider(create_gemini, api_key = "FAKE")
model <- provider$image_model("gemini-2.5-flash-image")
captured_body <- NULL
local_mocked_bindings(
post_to_api = function(url, headers, body, ...) {
captured_body <<- body
list(
candidates = list(list(
finishReason = "STOP",
content = list(parts = list(
list(inlineData = list(
mimeType = "image/png",
data = base64enc::base64encode(charToRaw("edited-image"))
))
))
))
)
}
)
result <- model$edit_image(
image = paste0(
"data:image/png;base64,",
base64enc::base64encode(charToRaw("source-image"))
),
prompt = "Make the mug blue.",
output_dir = tempdir()
)
expect_equal(captured_body$contents[[1]]$parts[[1]]$text, "Make the mug blue.")
expect_equal(captured_body$contents[[1]]$parts[[2]]$inlineData$mimeType, "image/png")
expect_true(file.exists(result$images[[1]]$path))
})
test_that("Gemini image model edit_image rejects masks for now", {
provider <- safe_create_provider(create_gemini, api_key = "FAKE")
model <- provider$image_model("gemini-2.5-flash-image")
expect_error(
model$edit_image(
image = "https://example.com/cat.png",
prompt = "Make it blue.",
mask = "https://example.com/mask.png"
),
"does not support `mask` yet"
)
})
# Live API tests (only run when API key is available)
test_that("Gemini provider can make real API calls", {
skip_if_no_api_key("Gemini")
skip_on_cran()
provider <- create_gemini()
model <- provider$language_model(gemini_model)
# Make a simple API call
result <- model$generate(
messages = list(
list(role = "user", content = "Say 'Test OK'")
),
max_tokens = 10
)
# Check that we got a response
expect_true(!is.null(result$text))
expect_true(nchar(result$text) > 0)
})
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.