Skip to content

Commit b9a821b

Browse files
authored
Merge pull request #51 from jhk0530/49-new-feature-request-reading-large-pdfs-stored-locally-in-vertex-ai
Update gemini_docs.R
2 parents 47cbc5a + d243f29 commit b9a821b

File tree

2 files changed

+133
-38
lines changed

2 files changed

+133
-38
lines changed

DESCRIPTION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Package: gemini.R
22
Title: Interface for 'Google Gemini' API
3-
Version: 0.15.1
3+
Version: 0.15.2
44
Authors@R: c(
55
person("Jinhwan", "Kim", , "hwanistic@gmail.com", role = c("aut", "cre", "cph"), comment = c(ORCID = "0009-0009-3217-2417")),
66
person("Maciej", "Nasinski", role = "ctb"))

R/gemini_docs.R

Lines changed: 132 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,21 @@
2626
#'
2727
#' @export
2828
#' @seealso https://ai.google.dev/gemini-api/docs/document-processing?lang=rest
29-
gemini_docs <- function(pdf_path, prompt, type = "PDF", api_key = Sys.getenv("GEMINI_API_KEY")) {
29+
gemini_docs <- function(pdf_path, prompt, type = "PDF", api_key = Sys.getenv("GEMINI_API_KEY"), large = FALSE, local = FALSE) {
30+
# If local = FALSE and input is a URL, download to a temp file
31+
temp_files <- character(0)
32+
if (!local) {
33+
# Download the file to a temp file
34+
temp_file <- tempfile(fileext = paste0(".", tools::file_ext(pdf_path)))
35+
download.file(pdf_path, temp_file, mode = "wb", quiet = TRUE)
36+
temp_files <- c(temp_files, temp_file)
37+
pdf_path <- temp_file
38+
39+
# Remove temp files on exit
40+
if (length(temp_files) > 0) on.exit(unlink(temp_files), add = TRUE)
41+
}
42+
43+
# Check file existence
3044
if (length(pdf_path) < 1) stop("At least one file path must be provided.")
3145
if (any(!file.exists(pdf_path))) stop("Some files do not exist: ", paste(pdf_path[!file.exists(pdf_path)], collapse = ", "))
3246

@@ -52,53 +66,92 @@ gemini_docs <- function(pdf_path, prompt, type = "PDF", api_key = Sys.getenv("GE
5266
# Use the first mime type if multiple are available
5367
mime_type <- if (is.character(mime_types[[type]])) mime_types[[type]][1] else as.character(mime_types[[type]][1])
5468

55-
# Base64 encode all files
56-
file_parts <- lapply(pdf_path, function(path) {
57-
list(
58-
inline_data = list(
59-
mime_type = mime_type,
60-
data = base64enc::base64encode(path)
69+
if (!large && !local) {
70+
# Base64 encode all files and send directly (for small files)
71+
file_parts <- lapply(pdf_path, function(path) {
72+
list(
73+
inline_data = list(
74+
mime_type = mime_type,
75+
data = base64enc::base64encode(path)
76+
)
6177
)
62-
)
63-
})
78+
})
6479

65-
# Add the prompt as the last part
66-
parts <- c(file_parts, list(list(text = prompt)))
80+
parts <- c(file_parts, list(list(text = prompt)))
6781

68-
# Prepare request body
69-
body <- list(
70-
contents = list(
71-
list(parts = parts)
82+
body <- list(
83+
contents = list(
84+
list(parts = parts)
85+
)
7286
)
73-
)
7487

75-
url <- "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent"
88+
url <- "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent"
7689

77-
req <- httr2::request(url) |>
78-
httr2::req_url_query(key = api_key) |>
79-
httr2::req_headers("Content-Type" = "application/json") |>
80-
httr2::req_body_json(body, auto_unbox = TRUE)
90+
req <- httr2::request(url) |>
91+
httr2::req_url_query(key = api_key) |>
92+
httr2::req_headers("Content-Type" = "application/json") |>
93+
httr2::req_body_json(body, auto_unbox = TRUE)
8194

82-
resp <- httr2::req_perform(req)
95+
resp <- httr2::req_perform(req)
8396

84-
if (resp$status_code != 200) {
85-
stop(paste0("Error in Gemini API request: Status code ", resp$status_code))
86-
}
97+
if (resp$status_code != 200) {
98+
stop(paste0("Error in Gemini API request: Status code ", resp$status_code))
99+
}
87100

88-
result <- httr2::resp_body_json(resp)
101+
result <- httr2::resp_body_json(resp)
102+
103+
# Extract summary text (robust extraction of all $text fields)
104+
out <- tryCatch(
105+
{
106+
texts <- lapply(result$candidates[[1]]$content$parts, function(part) part$text)
107+
texts <- texts[!sapply(texts, is.null)]
108+
paste(texts, collapse = "\n")
109+
},
110+
error = function(e) {
111+
paste(unlist(result$candidates[[1]]$content$parts), collapse = "\n")
112+
}
113+
)
114+
return(out)
115+
} else {
116+
# Use file upload API for large or local files (only one file supported)
117+
if (length(pdf_path) > 1) stop("Large/Local mode supports only one file at a time.")
118+
file_uri <- upload_api(pdf_path[1], api_key, mime_type)
119+
120+
# Build request body for Gemini API using file_uri
121+
body <- list(
122+
contents = list(list(
123+
parts = list(
124+
list(text = prompt),
125+
list(file_data = list(mime_type = mime_type, file_uri = file_uri))
126+
)
127+
))
128+
)
129+
130+
url <- "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent"
131+
132+
req <- httr2::request(url) |>
133+
httr2::req_url_query(key = api_key) |>
134+
httr2::req_headers("Content-Type" = "application/json") |>
135+
httr2::req_body_json(body, auto_unbox = TRUE)
89136

90-
# Extract summary text (robust extraction of all $text fields)
91-
out <- tryCatch(
92-
{
93-
texts <- lapply(result$candidates[[1]]$content$parts, function(part) part$text)
94-
texts <- texts[!sapply(texts, is.null)]
95-
paste(texts, collapse = "\n")
96-
},
97-
error = function(e) {
98-
paste(unlist(result$candidates[[1]]$content$parts), collapse = "\n")
137+
resp <- httr2::req_perform(req)
138+
if (resp$status_code != 200) {
139+
stop(paste0("Error in Gemini API request: Status code ", resp$status_code))
99140
}
100-
)
101-
return(out)
141+
142+
result <- httr2::resp_body_json(resp)
143+
out <- tryCatch(
144+
{
145+
texts <- lapply(result$candidates[[1]]$content$parts, function(part) part$text)
146+
texts <- texts[!sapply(texts, is.null)]
147+
paste(texts, collapse = "\n")
148+
},
149+
error = function(e) {
150+
paste(unlist(result$candidates[[1]]$content$parts), collapse = "\n")
151+
}
152+
)
153+
return(out)
154+
}
102155
}
103156

104157
#' @title Summarize or analyze documents using Vertex AI Gemini
@@ -192,4 +245,46 @@ gemini_docs.vertex <- function(file_uri, prompt, mime_type = "application/pdf",
192245
candidates <- result$candidates
193246
outputs <- unlist(lapply(candidates, function(candidate) candidate$content$parts))
194247
return(outputs)
248+
}
249+
250+
# Helper function for file upload API (Gemini/Vertex)
251+
upload_api <- function(local_file, api_key, mime_type) {
252+
# Start resumable upload session
253+
file_size <- file.info(local_file)$size
254+
meta_body <- list(file = list(display_name = basename(local_file)))
255+
meta_req <- httr2::request("https://generativelanguage.googleapis.com/upload/v1beta/files") |>
256+
httr2::req_url_query(key = api_key) |>
257+
httr2::req_headers(
258+
"X-Goog-Upload-Protocol" = "resumable",
259+
"X-Goog-Upload-Command" = "start",
260+
"X-Goog-Upload-Header-Content-Length" = as.character(file_size),
261+
"X-Goog-Upload-Header-Content-Type" = mime_type,
262+
"Content-Type" = "application/json"
263+
) |>
264+
httr2::req_body_json(meta_body, auto_unbox = TRUE)
265+
266+
meta_resp <- httr2::req_perform(meta_req)
267+
if (meta_resp$status_code != 200) {
268+
stop(paste0("Error starting upload session: Status code ", meta_resp$status_code))
269+
}
270+
upload_url <- httr2::resp_headers(meta_resp)[["x-goog-upload-url"]]
271+
if (is.null(upload_url)) stop("Failed to get upload URL from Gemini API.")
272+
273+
# Upload the file bytes
274+
upload_req <- httr2::request(upload_url) |>
275+
httr2::req_headers(
276+
"Content-Length" = as.character(file_size),
277+
"X-Goog-Upload-Offset" = "0",
278+
"X-Goog-Upload-Command" = "upload, finalize"
279+
) |>
280+
httr2::req_body_file(local_file)
281+
282+
upload_resp <- httr2::req_perform(upload_req)
283+
if (upload_resp$status_code != 200) {
284+
stop(paste0("Error uploading file: Status code ", upload_resp$status_code))
285+
}
286+
upload_result <- httr2::resp_body_json(upload_resp)
287+
file_uri <- upload_result$file$uri
288+
if (is.null(file_uri)) stop("Failed to get file_uri after upload.")
289+
return(file_uri)
195290
}

0 commit comments

Comments
 (0)