fmegahed Claude Opus 5.5 commited on
Commit
0cd0bb3
·
1 Parent(s): 74221ec

Say in the app that it has been updated since the paper

Browse files

The header links to a 'What changed' note built from the configuration: this
version and date, the version and model the paper describes, and why the two
differ. The README gains a version table.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Files changed (6) hide show
  1. R/config.R +15 -1
  2. R/help.R +22 -1
  3. README.md +14 -1
  4. app.R +2 -2
  5. tests/testthat/test-config-help.R +16 -3
  6. www/miami-theme.css +5 -0
R/config.R CHANGED
@@ -13,6 +13,16 @@ get_extraction_model = function() {
13
  if (nchar(model) == 0) DEFAULT_EXTRACTION_MODEL else model
14
  }
15
 
 
 
 
 
 
 
 
 
 
 
16
  # Name of the provider behind ellmer::chat_openai(), used in UI text
17
  MODEL_PROVIDER = "OpenAI"
18
 
@@ -50,6 +60,10 @@ app_config = function() {
50
  max_pages_or_images = MAX_PAGES_OR_IMAGES,
51
  max_upload_mb = MAX_UPLOAD_MB,
52
  max_fields = MAX_FIELDS,
53
- list_separator = LIST_SEPARATOR
 
 
 
 
54
  )
55
  }
 
13
  if (nchar(model) == 0) DEFAULT_EXTRACTION_MODEL else model
14
  }
15
 
16
+ # Version of this app, shown in the header and in the "What changed" help
17
+ APP_VERSION_NUMBER = "3.0.0"
18
+ APP_VERSION_DATE = "9 October 2026"
19
+
20
+ # The version that the paper describes. The app has been updated since the
21
+ # paper was drafted, and the "What changed" help says so. PAPER_MODEL is a
22
+ # record of the past, not a setting: nothing is sent to it.
23
+ PAPER_VERSION_NUMBER = "2.0.1"
24
+ PAPER_MODEL = "gpt-5-mini"
25
+
26
  # Name of the provider behind ellmer::chat_openai(), used in UI text
27
  MODEL_PROVIDER = "OpenAI"
28
 
 
60
  max_pages_or_images = MAX_PAGES_OR_IMAGES,
61
  max_upload_mb = MAX_UPLOAD_MB,
62
  max_fields = MAX_FIELDS,
63
+ list_separator = LIST_SEPARATOR,
64
+ version = APP_VERSION_NUMBER,
65
+ version_date = APP_VERSION_DATE,
66
+ paper_version = PAPER_VERSION_NUMBER,
67
+ paper_model = PAPER_MODEL
68
  )
69
  }
R/help.R CHANGED
@@ -1,7 +1,7 @@
1
  # Help text for the "?" buttons. Every number and name in the text comes from
2
  # app_config(), the same values that drive the app's behaviour.
3
 
4
- HELP_TOPICS = c("source", "fields", "extract", "results")
5
 
6
  # Returns list(title, paragraphs) for one help topic
7
  help_content = function(topic, cfg = app_config()) {
@@ -73,6 +73,21 @@ help_content = function(topic, cfg = app_config()) {
73
  "Download as CSV saves the table with the source_id, status and status_message columns. Reproduce in code shows R and Python code that runs the same extraction, with your fields and the same model, on your own data."
74
  )
75
  ),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
  stop("Unknown help topic: ", topic)
77
  )
78
  }
@@ -90,6 +105,12 @@ help_button = function(topic) {
90
  )
91
  }
92
 
 
 
 
 
 
 
93
  # Modal dialog with the help for a topic
94
  help_modal = function(topic, cfg = app_config()) {
95
  content = help_content(topic, cfg)
 
1
  # Help text for the "?" buttons. Every number and name in the text comes from
2
  # app_config(), the same values that drive the app's behaviour.
3
 
4
+ HELP_TOPICS = c("source", "fields", "extract", "results", "changes")
5
 
6
  # Returns list(title, paragraphs) for one help topic
7
  help_content = function(topic, cfg = app_config()) {
 
73
  "Download as CSV saves the table with the source_id, status and status_message columns. Reproduce in code shows R and Python code that runs the same extraction, with your fields and the same model, on your own data."
74
  )
75
  ),
76
+ changes = list(
77
+ title = "What changed since the paper",
78
+ paragraphs = c(
79
+ sprintf(
80
+ "This is version %s of the app, released on %s. The paper describes version %s, which used the %s model %s.",
81
+ cfg$version, cfg$version_date, cfg$paper_version, cfg$provider, cfg$paper_model
82
+ ),
83
+ sprintf(
84
+ "The model is now %s. If the screen or the model you see here differs from the paper, that is why.",
85
+ cfg$model
86
+ ),
87
+ "Other changes: ready-made field templates for common quality documents; field definitions that can be saved and loaded; a progress bar, with one failed item no longer discarding the rest; R and Python code that reproduces an extraction; and a '?' beside each step that explains how it works.",
88
+ "The difference is intentional. Language models are replaced often, and users find problems that only appear in use. An app built on a language model needs regular updating, and a paper can only describe it at one point in time."
89
+ )
90
+ ),
91
  stop("Unknown help topic: ", topic)
92
  )
93
  }
 
105
  )
106
  }
107
 
108
+ # Link in the header that opens the "What changed" help
109
+ changes_link = function() {
110
+ shiny::actionLink("help_changes", "Updated since the paper. What changed?",
111
+ class = "changes-link", `aria-haspopup` = "dialog")
112
+ }
113
+
114
  # Modal dialog with the help for a topic
115
  help_modal = function(topic, cfg = app_config()) {
116
  content = help_content(topic, cfg)
README.md CHANGED
@@ -74,7 +74,20 @@ one item per row of its first column, and its first row is treated as a header.
74
  The model id is set in one place, `DEFAULT_EXTRACTION_MODEL` in `R/config.R`. To use another
75
  OpenAI model without editing code, set the environment variable `EXTRACTION_MODEL`. The
76
  call, the help text and the generated code all read this value. A test fails if a model id
77
- is written anywhere else in the app code.
 
 
 
 
 
 
 
 
 
 
 
 
 
78
 
79
  ## Run it yourself
80
 
 
74
  The model id is set in one place, `DEFAULT_EXTRACTION_MODEL` in `R/config.R`. To use another
75
  OpenAI model without editing code, set the environment variable `EXTRACTION_MODEL`. The
76
  call, the help text and the generated code all read this value. A test fails if a model id
77
+ is written anywhere else in the app code, apart from the record of the model that the
78
+ paper's version used.
79
+
80
+ ## Versions
81
+
82
+ | Version | Date | Model | Notes |
83
+ |---|---|---|---|
84
+ | 3.0.0 | 9 October 2026 | `gpt-6-luna` | Released after the paper was drafted. Field templates, saved field definitions, per-item progress and failure handling, code that reproduces an extraction, and in-app help. |
85
+ | 2.0.1 | 2026 | `gpt-5-mini` | The version the paper describes. |
86
+
87
+ The app has been updated since the paper was drafted, and this is intentional. Language
88
+ models are replaced often, and users find problems that only appear in use, so an app built
89
+ on a language model needs regular updating. The header of the app links to a short "What
90
+ changed" note that is generated from the values in `R/config.R`.
91
 
92
  ## Run it yourself
93
 
app.R CHANGED
@@ -17,7 +17,7 @@ options(shiny.maxRequestSize = MAX_UPLOAD_MB * 1024^2)
17
  # Model used for every call and named in every piece of UI text
18
  EXTRACTION_MODEL = get_extraction_model()
19
 
20
- APP_VERSION = "Version 3.0.0 | October 2026"
21
 
22
  PAPER_TITLE = "What Quality Engineers Need to Know About Generative AI: Structured Data Extraction"
23
 
@@ -83,7 +83,7 @@ ui = shiny::fluidPage(
83
  ),
84
  shiny::div(
85
  class = "header-right",
86
- shiny::p(APP_VERSION),
87
  shiny::p(
88
  shiny::tags$strong("Authors: "),
89
  "Fadel M. Megahed, Ying-Ju Chen, Yamin Dahwich, Arthur Carvalho, L. Allison Jones-Farmer, Ibrahim Yousif, and Inez M. Zwetsloot"
 
17
  # Model used for every call and named in every piece of UI text
18
  EXTRACTION_MODEL = get_extraction_model()
19
 
20
+ APP_VERSION = paste0("Version ", APP_VERSION_NUMBER, " | ", APP_VERSION_DATE)
21
 
22
  PAPER_TITLE = "What Quality Engineers Need to Know About Generative AI: Structured Data Extraction"
23
 
 
83
  ),
84
  shiny::div(
85
  class = "header-right",
86
+ shiny::p(APP_VERSION, " | ", changes_link()),
87
  shiny::p(
88
  shiny::tags$strong("Authors: "),
89
  "Fadel M. Megahed, Ying-Ju Chen, Yamin Dahwich, Arthur Carvalho, L. Allison Jones-Farmer, Ibrahim Yousif, and Inez M. Zwetsloot"
tests/testthat/test-config-help.R CHANGED
@@ -48,11 +48,12 @@ test_that("no model id is written outside R/config.R", {
48
  )
49
  }
50
 
51
- # In the config file, the only model id is the default
 
52
  config_lines = readLines(config_file, warn = FALSE)
53
  hits = grep(model_pattern, config_lines, ignore.case = TRUE)
54
- expect_length(hits, 1)
55
- expect_match(config_lines[hits], "^DEFAULT_EXTRACTION_MODEL = ")
56
  })
57
 
58
  test_that("help text is generated from the configuration values", {
@@ -88,6 +89,18 @@ test_that("help text follows the configured model at run time", {
88
  expect_match(paste(help_content("source")$paragraphs, collapse = " "), "model-from-env")
89
  })
90
 
 
 
 
 
 
 
 
 
 
 
 
 
91
  test_that("every help topic has content, a button and a dialog, without em dashes", {
92
  for (topic in HELP_TOPICS) {
93
  content = help_content(topic)
 
48
  )
49
  }
50
 
51
+ # In the config file, the only model ids are the default and the record of
52
+ # the model that the paper's version used
53
  config_lines = readLines(config_file, warn = FALSE)
54
  hits = grep(model_pattern, config_lines, ignore.case = TRUE)
55
+ expect_length(hits, 2)
56
+ expect_match(config_lines[hits], "^(DEFAULT_EXTRACTION_MODEL|PAPER_MODEL) = ")
57
  })
58
 
59
  test_that("help text is generated from the configuration values", {
 
89
  expect_match(paste(help_content("source")$paragraphs, collapse = " "), "model-from-env")
90
  })
91
 
92
+ test_that("the header says the app was updated since the paper, from the configuration", {
93
+ text = paste(help_content("changes")$paragraphs, collapse = " ")
94
+ for (value in c(APP_VERSION_NUMBER, APP_VERSION_DATE, PAPER_VERSION_NUMBER, PAPER_MODEL, get_extraction_model())) {
95
+ expect_match(text, value, fixed = TRUE)
96
+ }
97
+ withr::local_envvar(EXTRACTION_MODEL = "model-from-env")
98
+ expect_match(paste(help_content("changes")$paragraphs, collapse = " "), "model-from-env")
99
+ link = as.character(changes_link())
100
+ expect_match(link, "id=\"help_changes\"", fixed = TRUE)
101
+ expect_match(link, "What changed?", fixed = TRUE)
102
+ })
103
+
104
  test_that("every help topic has content, a button and a dialog, without em dashes", {
105
  for (topic in HELP_TOPICS) {
106
  content = help_content(topic)
www/miami-theme.css CHANGED
@@ -65,6 +65,11 @@ body {
65
  text-align: right;
66
  }
67
 
 
 
 
 
 
68
  .header-right p {
69
  margin: 3px 0;
70
  font-size: 0.85em;
 
65
  text-align: right;
66
  }
67
 
68
+ .header-right .changes-link {
69
+ color: #EFDB72;
70
+ text-decoration: underline;
71
+ }
72
+
73
  .header-right p {
74
  margin: 3px 0;
75
  font-size: 0.85em;