Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Streaming revamp #227

Draft
wants to merge 10 commits into
base: main
Choose a base branch
from
Draft
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
6 changes: 3 additions & 3 deletions DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,14 @@ Imports:
glue,
htmltools,
htmlwidgets,
httr2,
httr2 (>= 1.0.3.9000),
ids,
jsonlite,
purrr,
R6 (>= 2.0),
rlang,
rstudioapi (>= 0.12),
shiny (>= 1.9.0),
shiny.i18n,
SSEparser,
stringr (>= 1.5.0),
utils,
yaml
Expand All @@ -57,6 +55,8 @@ Suggests:
withr
Config/testthat/edition: 3
Config/testthat/parallel: true
Remotes:
r-lib/httr2
Encoding: UTF-8
Language: en-US
LazyData: true
Expand Down
9 changes: 4 additions & 5 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,11 @@ S3method(list_available_models,ollama)
S3method(list_available_models,openai)
S3method(list_available_models,perplexity)
export(chat)
export(create_chat_azure_openai)
export(create_chat_google)
export(create_chat_ollama)
export(create_chat_openai)
export(create_completion_anthropic)
export(create_completion_azure_openai)
export(create_completion_google)
export(create_completion_huggingface)
export(get_available_endpoints)
export(get_available_models)
Expand All @@ -52,19 +54,16 @@ export(gptstudio_sitrep)
export(gptstudio_skeleton_build)
export(gptstudio_spelling_grammar)
export(input_audio_clip)
export(openai_create_chat_completion)
export(transcribe_audio)
import(cli)
import(htmltools)
import(htmlwidgets)
import(httr2)
import(rlang)
import(shiny)
importFrom(R6,R6Class)
importFrom(glue,glue)
importFrom(htmltools,div)
importFrom(htmltools,tag)
importFrom(htmltools,tagList)
importFrom(htmltools,tags)
importFrom(jsonlite,fromJSON)
importFrom(shiny,icon)
2 changes: 1 addition & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
- New styling of chat app. #224
- Add code syntax highlighting to chat app. #224
- Replace curl calls with httr2. #224
- Remove magrittr pipe in favor of base pipe, require R >= 4.1
- Replace %>% with |>, bump min R to >=4.1, remove revdep folder. #226

## gptstudio 0.4.0

Expand Down
172 changes: 73 additions & 99 deletions R/api_perform_request.R
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ gptstudio_request_perform <- function(skeleton, ...) {
#' @export
gptstudio_request_perform.gptstudio_request_openai <- function(skeleton, ...,
shiny_session = NULL) {
# Translate request

skeleton$history <- chat_history_append(
history = skeleton$history,
Expand All @@ -37,46 +36,12 @@ gptstudio_request_perform.gptstudio_request_openai <- function(skeleton, ...,
skeleton$history <- add_docs_messages_to_history(skeleton$history)
}

body <- list(
"model" = skeleton$model,
"stream" = skeleton$stream,
"messages" = skeleton$history,
"max_tokens" = skeleton$extras$max_tokens,
"n" = skeleton$extra$n
)

# Create request
request <- request(skeleton$url) |>
req_auth_bearer_token(skeleton$api_key) |>
req_body_json(body)

# Perform request
response <- NULL

if (isTRUE(skeleton$stream)) {
if (is.null(shiny_session)) stop("Stream requires a shiny session object")

stream_handler <- OpenaiStreamParser$new(
session = shiny_session,
user_prompt = skeleton$prompt
)

stream_chat_completion(
messages = skeleton$history,
element_callback = stream_handler$parse_sse,
model = skeleton$model,
openai_api_key = skeleton$api_key
)

response <- stream_handler$value
} else {
response_json <- request |>
req_perform() |>
resp_body_json()
response <- create_chat_openai(prompt = skeleton$history,
model = skeleton$model,
stream = skeleton$stream,
shiny_session = shiny_session,
user_prompt = skeleton$prompt)

response <- response_json$choices[[1]]$message$content
}
# return value
structure(
list(
skeleton = skeleton,
Expand Down Expand Up @@ -104,50 +69,68 @@ gptstudio_request_perform.gptstudio_request_huggingface <-
}

#' @export
gptstudio_request_perform.gptstudio_request_google <-
function(skeleton, ...) {
response <- create_completion_google(prompt = skeleton$prompt)
structure(
list(
skeleton = skeleton,
response = response
),
class = "gptstudio_response_google"
)
gptstudio_request_perform.gptstudio_request_google <- function(skeleton, ...) {
skeleton$history <- chat_history_append(
history = skeleton$history,
role = "user",
name = "user_message",
content = skeleton$prompt
)

if (getOption("gptstudio.read_docs")) {
skeleton$history <- add_docs_messages_to_history(skeleton$history)
}

#' @export
gptstudio_request_perform.gptstudio_request_anthropic <-
function(skeleton, ...) {
model <- skeleton$model
response <- create_chat_google(prompt = skeleton$history,
model = skeleton$model)

skeleton$history <- chat_history_append(
history = skeleton$history,
role = "user",
content = skeleton$prompt
)
structure(
list(
skeleton = skeleton,
response = response
),
class = "gptstudio_response_google"
)
}

# Anthropic does not have a system message, so convert it to user
system <-
purrr::keep(skeleton$history, function(x) x$role == "system") |>
purrr::pluck("content")
history <-
purrr::keep(skeleton$history, function(x) x$role %in% c("user", "assistant"))
#' @export
gptstudio_request_perform.gptstudio_request_anthropic <- function(skeleton,
shiny_session = NULL,
...) {
model <- skeleton$model
stream <- skeleton$stream
prompt <- skeleton$prompt

cli_inform(c("i" = "Using Anthropic API with {model} model"))
response <- create_completion_anthropic(
prompt = history,
system = system,
model = model
)
structure(
list(
skeleton = skeleton,
response = response
),
class = "gptstudio_response_anthropic"
)
}
skeleton$history <- chat_history_append(
history = skeleton$history,
role = "user",
content = skeleton$prompt
)

# Anthropic does not have a system message, so convert it to user
system <-
purrr::keep(skeleton$history, function(x) x$role == "system") |>
purrr::pluck("content")
history <-
purrr::keep(skeleton$history, function(x) x$role %in% c("user", "assistant"))

cli_inform(c("i" = "Using Anthropic API with {model} model"))
response <- create_completion_anthropic(
prompt = history,
system = system,
model = model,
stream = stream,
shiny_session = shiny_session,
user_prompt = prompt
)
structure(
list(
skeleton = skeleton,
response = response
),
class = "gptstudio_response_anthropic"
)
}

#' @export
gptstudio_request_perform.gptstudio_request_azure_openai <- function(skeleton,
Expand All @@ -161,25 +144,16 @@ gptstudio_request_perform.gptstudio_request_azure_openai <- function(skeleton,
content = skeleton$prompt
)

if (isTRUE(skeleton$stream)) {
if (is.null(shiny_session)) stop("Stream requires a shiny session object")

stream_handler <- OpenaiStreamParser$new(
session = shiny_session,
user_prompt = skeleton$prompt
)

stream_azure_openai(
messages = skeleton$history,
element_callback = stream_handler$parse_sse
)

response <- stream_handler$value
} else {
response <- query_api_azure_openai(request_body = skeleton$history)
response <- response$choices[[1]]$message$content
if (getOption("gptstudio.read_docs")) {
skeleton$history <- add_docs_messages_to_history(skeleton$history)
}

response <- create_chat_azure_openai(prompt = skeleton$history,
model = skeleton$model,
stream = skeleton$stream,
shiny_session = shiny_session,
user_prompt = skeleton$prompt)

structure(
list(
skeleton = skeleton,
Expand All @@ -205,9 +179,9 @@ gptstudio_request_perform.gptstudio_request_ollama <- function(skeleton, ...,
skeleton$history <- add_docs_messages_to_history(skeleton$history)
}

response <- ollama_chat(
response <- create_chat_ollama(
model = skeleton$model,
messages = skeleton$history,
prompt = skeleton$history,
stream = skeleton$stream,
shiny_session = shiny_session,
user_prompt = skeleton$prompt
Expand All @@ -217,7 +191,7 @@ gptstudio_request_perform.gptstudio_request_ollama <- function(skeleton, ...,
structure(
list(
skeleton = skeleton,
response = response$message$content
response = response
),
class = "gptstudio_response_ollama"
)
Expand Down
50 changes: 21 additions & 29 deletions R/api_process_response.R
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,18 @@
}

#' @export
gptstudio_response_process.gptstudio_response_openai <-
function(skeleton, ...) {
gptstudio_response_process.gptstudio_response_openai <- function(skeleton, ...) {
last_response <- skeleton$response

Check warning on line 23 in R/api_process_response.R

View workflow job for this annotation

GitHub Actions / lint

file=R/api_process_response.R,line=23,col=4,[indentation_linter] Indentation should be 2 spaces but is 4 spaces.
skeleton <- skeleton$skeleton

new_history <- chat_history_append(

Check warning on line 26 in R/api_process_response.R

View workflow job for this annotation

GitHub Actions / lint

file=R/api_process_response.R,line=26,col=4,[indentation_linter] Indentation should be 2 spaces but is 4 spaces.
history = skeleton$history,
role = "assistant",
name = "assistant",
content = last_response
)

skeleton$history <- new_history

Check warning on line 33 in R/api_process_response.R

View workflow job for this annotation

GitHub Actions / lint

file=R/api_process_response.R,line=33,col=4,[indentation_linter] Indentation should be 2 spaces but is 4 spaces.
skeleton$prompt <- NULL # remove the last prompt
class(skeleton) <- c(
"gptstudio_request_skeleton",
Expand All @@ -41,13 +40,12 @@
}

#' @export
gptstudio_response_process.gptstudio_response_huggingface <-
function(skeleton, ...) {
gptstudio_response_process.gptstudio_response_huggingface <- function(skeleton, ...) {
response <- skeleton$response

Check warning on line 44 in R/api_process_response.R

View workflow job for this annotation

GitHub Actions / lint

file=R/api_process_response.R,line=44,col=4,[indentation_linter] Indentation should be 2 spaces but is 4 spaces.
skeleton <- skeleton$skeleton
last_response <- response[[1]]$generated_text

new_history <- c(

Check warning on line 48 in R/api_process_response.R

View workflow job for this annotation

GitHub Actions / lint

file=R/api_process_response.R,line=48,col=4,[indentation_linter] Indentation should be 2 spaces but is 4 spaces.
skeleton$history,
list(
list(role = "user", content = skeleton$prompt),
Expand All @@ -55,7 +53,7 @@
)
)

skeleton$history <- new_history

Check warning on line 56 in R/api_process_response.R

View workflow job for this annotation

GitHub Actions / lint

file=R/api_process_response.R,line=56,col=4,[indentation_linter] Indentation should be 2 spaces but is 4 spaces.
skeleton$prompt <- NULL # remove the last prompt
class(skeleton) <- c(
"gptstudio_request_skeleton",
Expand All @@ -65,53 +63,48 @@
}

#' @export
gptstudio_response_process.gptstudio_response_anthropic <-
function(skeleton, ...) {
gptstudio_response_process.gptstudio_response_anthropic <- function(skeleton, ...) {
last_response <- skeleton$response

Check warning on line 67 in R/api_process_response.R

View workflow job for this annotation

GitHub Actions / lint

file=R/api_process_response.R,line=67,col=4,[indentation_linter] Indentation should be 2 spaces but is 4 spaces.
skeleton <- skeleton$skeleton

new_history <- chat_history_append(

Check warning on line 70 in R/api_process_response.R

View workflow job for this annotation

GitHub Actions / lint

file=R/api_process_response.R,line=70,col=4,[indentation_linter] Indentation should be 2 spaces but is 4 spaces.
history = skeleton$history,
role = "assistant",
content = last_response
)

skeleton$history <- new_history

Check warning on line 76 in R/api_process_response.R

View workflow job for this annotation

GitHub Actions / lint

file=R/api_process_response.R,line=76,col=4,[indentation_linter] Indentation should be 2 spaces but is 4 spaces.
skeleton$prompt <- NULL # remove the last prompt
class(skeleton) <- c(
"gptstudio_request_skeleton",
"gptstudio_request_anthropic"
)

skeleton

Check warning on line 83 in R/api_process_response.R

View workflow job for this annotation

GitHub Actions / lint

file=R/api_process_response.R,line=83,col=4,[indentation_linter] Indentation should be 2 spaces but is 4 spaces.
}

#' @export
gptstudio_response_process.gptstudio_response_google <-
function(skeleton, ...) {
response <- skeleton$response
skeleton <- skeleton$skeleton
gptstudio_response_process.gptstudio_response_google <- function(skeleton, ...) {
last_response <- skeleton$response
skeleton <- skeleton$skeleton

new_history <- c(
skeleton$history,
list(
list(role = "user", content = skeleton$prompt),
list(role = "assistant", content = response)
)
)
new_history <- chat_history_append(
history = skeleton$history,
role = "assistant",
content = last_response
)

skeleton$history <- new_history
skeleton$prompt <- NULL # remove the last prompt
class(skeleton) <- c(
"gptstudio_request_skeleton",
"gptstudio_request_google"
)
skeleton
}
skeleton$history <- new_history
skeleton$prompt <- NULL # remove the last prompt
class(skeleton) <- c(
"gptstudio_request_skeleton",
"gptstudio_request_google"
)
skeleton
}

#' @export
gptstudio_response_process.gptstudio_response_azure_openai <-
function(skeleton, ...) {
gptstudio_response_process.gptstudio_response_azure_openai <- function(skeleton, ...) {
last_response <- skeleton$response
skeleton <- skeleton$skeleton

Expand Down Expand Up @@ -153,8 +146,7 @@
}

#' @export
gptstudio_response_process.gptstudio_response_perplexity <-
function(skeleton, ...) {
gptstudio_response_process.gptstudio_response_perplexity <- function(skeleton, ...) {
response <- skeleton$response
skeleton <- skeleton$skeleton

Expand Down
2 changes: 1 addition & 1 deletion R/api_skeletons.R
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ gptstudio_create_skeleton <- function(service = "openai",
prompt = prompt,
history = history,
# forcing false until streaming implemented for anthropic
stream = FALSE
stream = stream
),
"google" = new_gptstudio_request_skeleton_google(
model = model,
Expand Down
4 changes: 0 additions & 4 deletions R/gptstudio-package.R
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,3 @@
#' @importFrom glue glue
## gptstudio namespace: end
NULL

dummy <- function() {
SSEparser::SSEparser
}
Loading
Loading