Skip to content

Commit c80bf66

Browse files
Merge pull request #5 from Pandora-IsoMemo/feat/4_local_llm
llmModule 25.06.0: Feat/4 local llm
2 parents 9607dd1 + f38826e commit c80bf66

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+1984
-600
lines changed

.Rbuildignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
^.*\.Rproj$
22
^Dockerfile$
3+
^docker-compose.yml
34
^Jenkinsfile$
45
^JenkinsfileMPI$
56
^bin$
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
name: Docker
2+
3+
# This workflow uses actions that are not certified by GitHub.
4+
# They are provided by a third-party and are governed by
5+
# separate terms of service, privacy policy, and support
6+
# documentation.
7+
8+
on:
9+
schedule:
10+
- cron: "0 0 * * 0"
11+
push:
12+
branches: [main, beta]
13+
# Publish semver tags as releases.
14+
tags: ["v*.*.*"]
15+
workflow_dispatch: # manually running a workflow
16+
17+
env:
18+
# Use docker.io for Docker Hub if empty
19+
REGISTRY: ghcr.io
20+
# github.repository as <account>/<repo>
21+
IMAGE_NAME: ${{ github.repository }}
22+
23+
jobs:
24+
build:
25+
26+
runs-on: ubuntu-latest
27+
permissions:
28+
contents: read
29+
packages: write
30+
# This is used to complete the identity challenge
31+
# with sigstore/fulcio when running outside of PRs.
32+
id-token: write
33+
34+
steps:
35+
- name: Checkout repository
36+
uses: actions/checkout@v3
37+
38+
# Install the cosign tool except on PR
39+
# https://github.com/sigstore/cosign-installer
40+
- name: Install cosign
41+
if: github.event_name != 'pull_request'
42+
uses: sigstore/cosign-installer@v3.1.1
43+
with:
44+
cosign-release: 'v2.1.1'
45+
46+
47+
# Workaround: https://github.com/docker/build-push-action/issues/461
48+
- name: Setup Docker buildx
49+
uses: docker/setup-buildx-action@v2.5.0
50+
51+
# Login against a Docker registry except on PR
52+
# https://github.com/docker/login-action
53+
- name: Log into registry ${{ env.REGISTRY }}
54+
if: github.event_name != 'pull_request'
55+
uses: docker/login-action@v2.1.0
56+
with:
57+
registry: ${{ env.REGISTRY }}
58+
username: ${{ github.actor }}
59+
password: ${{ secrets.GITHUB_TOKEN }}
60+
61+
# Extract metadata (tags, labels) for Docker
62+
# https://github.com/docker/metadata-action
63+
- name: Extract Docker metadata
64+
id: meta
65+
uses: docker/metadata-action@v4.4.0
66+
with:
67+
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
68+
69+
# Build and push Docker image with Buildx (don't push on PR)
70+
# https://github.com/docker/build-push-action
71+
- name: Build and push Docker image
72+
id: build-and-push
73+
uses: docker/build-push-action@v4.0.0
74+
with:
75+
context: .
76+
push: ${{ github.event_name != 'pull_request' }}
77+
tags: ${{ steps.meta.outputs.tags }}
78+
labels: ${{ steps.meta.outputs.labels }}

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
.env
12
.Rproj.user
23
.Rhistory
34
.RData

DESCRIPTION

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Package: llmModule
22
Type: Package
33
Title: R Interface for Large Language Model APIs
4-
Version: 25.04.0
4+
Version: 25.06.0
55
Authors@R: c(
66
person("Ricardo", "Fernandes", email = "ldv1452@gmail.com", role = c("aut", "cre")),
77
person("Antonia", "Runge", email = "antonia.runge@inwt-statistics.de", role = c("aut"))
@@ -16,6 +16,7 @@ Depends: R (>= 3.5.0)
1616
Imports:
1717
data.table,
1818
httr2,
19+
ollamar,
1920
shiny,
2021
shinyAce,
2122
shinyjs

Dockerfile

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,12 @@ FROM ghcr.io/pandora-isomemo/base-image:latest
22

33
ADD . .
44

5+
# Install ollamar from GitHub
6+
RUN Rscript -e "remotes::install_github('hauselin/ollama-r')"
7+
58
RUN installPackage
9+
10+
# Expose ports
11+
EXPOSE 3838
12+
13+
CMD ["Rscript", "-e", "library(shiny); llmModule::startApplication(3838, host = '0.0.0.0')"]

NAMESPACE

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,43 @@
11
# Generated by roxygen2: do not edit by hand
22

33
S3method(as_table,LlmResponse)
4-
S3method(print,LlmApi)
5-
S3method(print,LlmPromptSettings)
4+
S3method(get_llm_models,LocalLlmApi)
5+
S3method(get_llm_models,RemoteLlmApi)
6+
S3method(print,LlmPromptConfig)
67
S3method(print,LlmResponse)
8+
S3method(print,LocalLlmApi)
9+
S3method(print,OllamaModel)
10+
S3method(print,OllamaModelManager)
11+
S3method(print,RemoteLlmApi)
12+
S3method(send_prompt,LocalLlmApi)
13+
S3method(send_prompt,RemoteLlmApi)
14+
S3method(update,OllamaModelManager)
715
export(as_table)
8-
export(new_LlmApi)
9-
export(new_LlmPromptSettings)
16+
export(get_llm_models)
17+
export(llm_generate_prompt_server)
18+
export(llm_generate_prompt_ui)
19+
export(new_LlmPromptConfig)
1020
export(new_LlmResponse)
21+
export(new_LocalLlmApi)
22+
export(new_OllamaModel)
23+
export(new_OllamaModelManager)
24+
export(new_RemoteLlmApi)
25+
export(send_prompt)
26+
export(startApplication)
27+
export(update)
1128
import(shiny)
1229

1330
importFrom(data.table,data.table)
1431
importFrom(data.table,rbindlist)
1532
importFrom(httr2,req_body_json)
1633
importFrom(httr2,req_headers)
1734
importFrom(httr2,req_perform)
35+
importFrom(httr2,req_timeout)
1836
importFrom(httr2,request)
1937
importFrom(httr2,resp_body_json)
38+
importFrom(ollamar,list_models)
39+
importFrom(ollamar,pull)
40+
importFrom(ollamar,test_connection)
2041
importFrom(shinyAce,aceEditor)
2142
importFrom(shinyjs,disable)
2243
importFrom(shinyjs,enable)

NEWS.md

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,26 @@
1+
# llmModule 25.06.0
2+
3+
## New features
4+
5+
* Added option to use LLM models from a local installation of Ollama:
6+
7+
* New classes `LocalLlmApi`, `OllamaModel`, and `OllamaModelManager` provide support for model configuration, and response parsing using locally hosted models.
8+
* The `llm_api_ui/server`, `llm_prompt_config_ui/server` and `llm_generate_prompt_ui/server` functions now detect and support Ollama-based backends.
9+
10+
## Bug fixes
11+
12+
* Fixed issue where the 'Generate Text' button remained enabled even when no valid API key was configured.
13+
14+
115
# llmModule 25.04.0
216

317
## New features
418

5-
- added method to format the API response (#1)
19+
* added method to format the API response (#1)
620

721
# llmModule 0.1.0
822

923
## New features
1024

11-
- first draft of the llm module
25+
* first draft of the llm module
1226

R/00-LlmApi-helpers.R

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
categorize_model <- function(id) {
2+
if (grepl("^gpt-[0-9.]+", id)) {
3+
match <- regmatches(id, regexpr("^gpt-[0-9.]+", id))
4+
return(toupper(match)) # Return as "GPT-4", "GPT-3.5", etc.
5+
}
6+
if (grepl("davinci|curie|babbage|ada", id)) return("GPT-3")
7+
if (grepl("embedding", id)) return("Embedding")
8+
if (grepl("whisper|speech", id)) return("Audio")
9+
if (grepl("dall-e|image", id)) return("Image")
10+
return("Other")
11+
}
12+
13+
# order list by category, start with models "GPT*" in decreasing order of version then other categories
14+
order_categories <- function(categories) {
15+
# Extract unique category names
16+
unique_cats <- unique(categories)
17+
18+
# Separate GPT-* from others
19+
gpt_cats <- grep("^GPT-[0-9.]+", unique_cats, value = TRUE)
20+
other_cats <- setdiff(unique_cats, gpt_cats)
21+
22+
# Sort GPT categories by descending version number
23+
# Convert "GPT-4" → 4.0, "GPT-3.5" → 3.5
24+
gpt_versions <- as.numeric(sub("GPT-", "", gpt_cats))
25+
ordered_gpt <- gpt_cats[order(-gpt_versions)] # decreasing
26+
27+
# Final category order
28+
ordered_categories <- c(ordered_gpt, sort(other_cats))
29+
30+
ordered_categories
31+
}
32+
33+
extract_named_model_list <- function(models, categories) {
34+
if (all(unique(categories) %in% c("Other"))) {
35+
return(models)
36+
}
37+
38+
# format into named list
39+
models_list <- split(models, categories)
40+
41+
# order list by category, start with models "GPT*" in decreasing order of version then other categories
42+
models_list <- models_list[order_categories(categories)]
43+
44+
return(models_list)
45+
}
46+
47+
llm_filter_config <- function(api, config) {
48+
provider <- api$provider # e.g., "OpenAI", "DeepSeek", "Ollama"
49+
50+
supported <- switch(
51+
provider,
52+
"OpenAI" = c("model", "messages", "max_tokens", "temperature", "top_p", "n", "stop", "seed",
53+
"presence_penalty", "frequency_penalty", "logprobs"),
54+
"DeepSeek" = c("model", "messages", "max_tokens", "temperature", "top_p", "n", "stop", "seed"),
55+
"Ollama" = c("model", "messages", "max_tokens", "temperature", "top_p", "stop", "seed"),
56+
character(0)
57+
)
58+
59+
all_fields <- names(config)
60+
unsupported <- setdiff(all_fields, supported)
61+
62+
result <- config[names(config) %in% supported]
63+
64+
if (length(unsupported) > 0) {
65+
warning_msg <- sprintf("The following inputs are ignored for provider '%s': %s",
66+
provider,
67+
paste(unsupported, collapse = ", "))
68+
warning(warning_msg, call. = FALSE)
69+
#result <- append_attr(result, warning_msg, "message")
70+
}
71+
72+
return(result)
73+
}
74+
75+
# Append attribute to object
76+
append_attr <- function(object, val, attr_name) {
77+
existing <- attr(object, attr_name)
78+
attr(object, attr_name) <- c(existing, val)
79+
object
80+
}

R/00-Namespace.R

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
#' @rawNamespace import(shiny)
22
#'
33
#' @importFrom data.table data.table rbindlist
4-
#' @importFrom httr2 req_body_json req_headers req_perform request resp_body_json
4+
#' @importFrom httr2 req_body_json req_headers req_perform req_timeout request resp_body_json
5+
#' @importFrom ollamar list_models pull test_connection
56
#' @importFrom shinyAce aceEditor
67
#' @importFrom shinyjs disable enable
78
#'

0 commit comments

Comments
 (0)