Skip to content

Commit 28e9007

Browse files
committed
feat(gemini): adopt 3.7 Flash and clamp thinking levels per model
Gemini 3.7 Flash replaces 3.6 everywhere a Flash run can be started: the four text tiers, GEMINI_DOCUMENT_MODELS, HTR, magazine-issue extraction, YouTube transcription's non-French slot, and the audio/video pipelines. Its authority item is 111774, verified live along with the annotation payloads for iwac:ocrModel, iwac:summaryModel and iwac:transcriptionModel. 3.7 dropped the MINIMAL thinking level, and gemini-flash-latest rolled onto 3.7 the same day. Every Gemini path here hardcoded MINIMAL, so OCR extraction, HTR, audio, video and all four text tiers were returning 400 INVALID_ARGUMENT on every call before this change. ModelOption now carries supported_thinking_levels, filled in from a live probe of all nine Gemini/Gemma models rather than inferred from their names, and clamp_thinking_level() snaps a request to the nearest rung the model has, rounding up on a tie. Pipelines go on asking for "minimal" to mean "as little as this model offers". The clamp replaces the hand-coded Gemma MINIMAL/HIGH mapping in the adapter and is shared with gemini_utils, which is what fixes the multimodal side. Tiers now offer the pinned gemini-3.7-flash rather than the rolling gemini-flash: whatever a tier picks is what step 03 stamps into an iwac:*Model annotation, and a rolling alias reports its own version as the string "Gemini Flash Latest". The rolling entry stays in MODEL_REGISTRY for runs that stamp nothing, and TEXT_FULL_MODELS keeps it on purpose because OCR correction writes no annotation of its own. A new test asserts every Gemini key a provenance-stamping tier can pick also has an authority item. It failed on the rolling gemini-pro, which sat in GEMINI_DOCUMENT_MODELS even though AI_ocr_extraction/03 stamps iwac:ocrModel — same bug class, so that is pinned to gemini-3.1-pro too. gemini-3.6-flash stays in AI_MODEL_ITEMS: 10 YouTube transcripts already on the archive annotate item 79611, and OCR text produced by 3.6 that has not reached step 03 must be stamped for the model that actually read it. Three display_title values that had drifted from their live Omeka titles are corrected alongside it (3.1 pro, 3.6 flash, 3.1 flash lite). The Mooré comparison and throughput figures in the YouTube README were measured on 3.6 and are left attributed to it rather than re-attributed to a model that has not been benchmarked here.
1 parent 7445f23 commit 28e9007

31 files changed

Lines changed: 420 additions & 145 deletions

AI_NER/01_NER_AI.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
44
Supported models:
55
- gpt-5.6-luna: Fast, cost-effective OpenAI model (GPT-5.6 high-volume tier)
6-
- gemini-flash: Fast, cost-effective Gemini model
6+
- gemini-3.7-flash: Fast, cost-effective Gemini model
77
- gemma-4: Google Gemma 4 31B — open-weights flagship, via Gemini API
88
- mistral-large: Mistral Large 3 flagship model
99
- ministral-14b: Ministral 3 14B cost-effective model
@@ -29,7 +29,7 @@
2929
Usage examples:
3030
python 01_NER_AI.py --item-set-id 123
3131
python 01_NER_AI.py --item-set-id 123 --model gpt-5.6-luna
32-
python 01_NER_AI.py --item-set-id 123 --model gemini-flash --async
32+
python 01_NER_AI.py --item-set-id 123 --model gemini-3.7-flash --async
3333
python 01_NER_AI.py --item-set-id 123 --model gemma-4
3434
python 01_NER_AI.py --item-set-id 123 --model mistral-large
3535
python 01_NER_AI.py --item-set-id 123 --model ministral-14b

AI_NER/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ different provenance; use `--force` to replace it deliberately.
5050

5151
| Model | Provider | Speed | Cost |
5252
|-------|----------|-------|------|
53-
| `gemini-flash` | Google | Fast | Low |
53+
| `gemini-3.7-flash` | Google | Fast | Low |
5454
| `gemma-4` | Google (Gemma 4 31B, open-weights via Gemini API) | Fast | Low |
5555
| `gpt-5.6-luna` | OpenAI | Fast | Low |
5656
| `mistral-large` | Mistral | Medium | Medium |
@@ -61,7 +61,7 @@ different provenance; use `--force` to replace it deliberately.
6161

6262
All models use the same French-language prompt (`ner_system_prompt.md`) optimized for West African Islamic contexts.
6363

64-
`gemma-4` uses the same `GEMINI_API_KEY` as the Gemini models. Thinking level is `minimal` by default (Gemma 4 accepts only `MINIMAL` or `HIGH`), which matches the low-cost entity-extraction budget used by `gemini-flash`.
64+
`gemma-4` uses the same `GEMINI_API_KEY` as the Gemini models. Thinking level is `minimal` by default (Gemma 4 accepts only `MINIMAL` or `HIGH`), which matches the low-cost entity-extraction budget used by `gemini-3.7-flash`.
6565

6666
`qwen3.5-moe` and `deepseek-v4-flash-0731` share one `OPENROUTER_API_KEY` and cost
6767
roughly a tenth of `gpt-5.6-luna`, which is what makes a full-corpus pass

AI_audio_summary/02_AI_transcribe_audio.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,14 @@ def __init__(
104104
105105
Args:
106106
api_key (str, optional): Gemini API key. If None, will use GEMINI_API_KEY environment variable.
107-
model (str, optional): Model to use — 'gemini-pro-latest', 'gemini-flash-latest', or
107+
model (str, optional): Model to use — 'gemini-pro-latest', 'gemini-3.7-flash', or
108108
'gemini-flash-lite-latest'. Default is 'gemini-pro-latest'.
109+
The Flash slot names a pinned release where the other two roll,
110+
because each transcript records ``Generated using: Google
111+
<model>`` in its header — and "gemini-flash-latest" names no
112+
version a reader could look up later. Pro and Flash-Lite are
113+
still rolling for now; pin them when their headers start being
114+
cited too.
109115
requests_per_minute: Optional RPM limit for proactive throttling (None = no throttling)
110116
transcription_prompt: The transcription prompt to use (selected in ``main()``);
111117
falls back to ``DEFAULT_PROMPT``.
@@ -712,7 +718,7 @@ def parse_args():
712718
)
713719
parser.add_argument(
714720
"--model",
715-
choices=["gemini-pro-latest", "gemini-flash-latest", "gemini-flash-lite-latest"],
721+
choices=["gemini-pro-latest", "gemini-3.7-flash", "gemini-flash-lite-latest"],
716722
default=None,
717723
help="Model to use for transcription (default: interactive selection)"
718724
)
@@ -754,7 +760,7 @@ def select_model_interactive():
754760
models_table.add_column("Model", style="green")
755761
models_table.add_column("Description", style="dim")
756762
models_table.add_row("1", "gemini-pro-latest", "Higher quality, slower")
757-
models_table.add_row("2", "gemini-flash-latest", "Faster, good quality")
763+
models_table.add_row("2", "gemini-3.7-flash", "Faster, good quality")
758764
models_table.add_row("3", "gemini-flash-lite-latest", "Fastest, cheapest, lowest latency")
759765
console.print(models_table)
760766

@@ -763,7 +769,7 @@ def select_model_interactive():
763769
).strip()
764770

765771
if model_choice == '2':
766-
return 'gemini-flash-latest'
772+
return 'gemini-3.7-flash'
767773
if model_choice == '3':
768774
return 'gemini-flash-lite-latest'
769775
return 'gemini-pro-latest'

AI_audio_summary/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ Select mode interactively or edit prompt files in `prompts/`.
6969
| Model | Speed | Quality | Cost |
7070
|-------|-------|---------|------|
7171
| `gemini-flash-lite-latest` | Fastest | Good | ~$0.20-1/hour |
72-
| `gemini-flash-latest` | Faster | Good | ~$0.50-2/hour |
72+
| `gemini-3.7-flash` | Faster | Good | ~$0.50-2/hour |
7373
| `gemini-pro-latest` | Slower | Higher | ~$5-15/hour |
7474

7575
Use Flash-Lite for clean, single-speaker recordings on a budget, Flash for general use, Pro for noisy audio or multiple speakers.

AI_htr_extraction/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ Each mode uses a specialized prompt (`htr_system_prompt_*.md`) with language-spe
4949

5050
| Model | Speed | Accuracy | Best For |
5151
|-------|-------|----------|----------|
52-
| Gemini Flash | Faster | Good | Clear handwriting, bulk processing |
52+
| Gemini 3.7 Flash | Faster | Good | Clear handwriting, bulk processing |
5353
| Gemini Pro | Slower | Higher | Difficult scripts, faded ink |
5454

5555
## Output

AI_htr_extraction/gemini_htr_processor.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,11 @@
7272
"multilingual": ("text (detect language automatically)", "htr_system_prompt_multilingual.md"),
7373
}
7474

75-
# Gemini models available for HTR, keyed by llm_provider registry key.
76-
HTR_MODELS = ["gemini-flash", "gemini-pro"]
75+
# Gemini models available for HTR, keyed by llm_provider registry key. Flash is
76+
# the pinned 3.7 rather than the rolling ``gemini-flash``: HTR output is written
77+
# back under an ``iwac:ocrModel`` annotation, and a rolling alias reports its own
78+
# version as "Gemini Flash Latest".
79+
HTR_MODELS = ["gemini-3.7-flash", "gemini-3.1-pro"]
7780

7881

7982
def load_system_instruction(language: str) -> str:

AI_ocr_correction/02_correct_alto_xml.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
1515
Supports multiple models via --model flag:
1616
- deepseek-v4-flash-0731 (default): DeepSeek official Flash release via OpenRouter
17-
- gemini-flash: Gemini Flash - fast, cost-effective
17+
- gemini-3.7-flash: Gemini 3.7 Flash - fast, cost-effective
1818
- gemini-pro: Gemini Pro - highest quality
1919
- gpt-5.6-luna: OpenAI GPT-5.6 Luna - cost-optimized tier
2020
- gpt-5.6-sol: OpenAI GPT-5.6 Sol - flagship tier
@@ -625,7 +625,7 @@ def parse_args() -> argparse.Namespace:
625625
python 02_correct_alto_xml.py
626626
627627
# Use Gemini Flash (fast, no thinking)
628-
python 02_correct_alto_xml.py --model gemini-flash
628+
python 02_correct_alto_xml.py --model gemini-3.7-flash
629629
630630
# Custom directories and max lines per request
631631
python 02_correct_alto_xml.py --input-dir ./ALTO --output-dir ./ALTO_Corrected --max-lines 40
@@ -690,7 +690,9 @@ def main():
690690
# Configure LLM based on provider. Temperature is deliberately absent: it is
691691
# set per model in MODEL_REGISTRY from each vendor's guidance, and forcing it
692692
# low here is what Gemini 3 and Qwen both document as a cause of looping.
693-
if model_option.key == "gemini-flash":
693+
# "minimal" here means "as little as this model offers" — Gemini 3.7 Flash
694+
# dropped that rung, and the registry clamp turns the request into LOW.
695+
if model_option.key == "gemini-3.7-flash":
694696
config = LLMConfig(thinking_level="minimal") # Fastest/cheapest
695697
elif model_option.key == "gemini-pro":
696698
config = LLMConfig(thinking_level="low") # Minimal thinking
@@ -707,8 +709,8 @@ def main():
707709
config_table.add_row("Input Directory", str(input_dir))
708710
config_table.add_row("Output Directory", str(output_dir))
709711
config_table.add_row("Max Lines/Request", f"{args.max_lines} (for large blocks)")
710-
if model_option.key == "gemini-flash":
711-
config_table.add_row("Thinking", "Minimal (thinking_level=minimal)")
712+
if model_option.key == "gemini-3.7-flash":
713+
config_table.add_row("Thinking", "Shallowest available (thinking_level=low)")
712714
elif model_option.key == "gemini-pro":
713715
config_table.add_row("Thinking Level", "low")
714716
console.print(config_table)

AI_ocr_correction/02_correct_ocr_text.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
88
Supports multiple models via --model flag:
99
- deepseek-v4-flash-0731 (default): DeepSeek official Flash release via OpenRouter
10-
- gemini-flash: Gemini Flash - fast, cost-effective
10+
- gemini-3.7-flash: Gemini 3.7 Flash - fast, cost-effective
1111
- gemini-pro: Gemini Pro - highest quality
1212
- gpt-5.6-luna: OpenAI GPT-5.6 Luna - cost-optimized tier
1313
- gpt-5.6-sol: OpenAI GPT-5.6 Sol - flagship tier
@@ -308,11 +308,12 @@ def main():
308308
sys.exit(1)
309309

310310
# Configure LLM based on provider
311-
# For Gemini Flash: disable thinking for faster, cheaper processing
311+
# For Gemini Flash: as little thinking as the model offers, for speed/cost.
312+
# 3.7 dropped MINIMAL, so the registry clamp turns that request into LOW.
312313
# For other models: use appropriate defaults
313314
# Temperature is left to MODEL_REGISTRY, which carries each vendor's own
314315
# recommendation — pinning it low here is a documented cause of looping.
315-
if model_option.key == "gemini-flash":
316+
if model_option.key == "gemini-3.7-flash":
316317
config = LLMConfig(thinking_level="minimal") # Fastest/cheapest
317318
elif model_option.key == "gemini-pro":
318319
config = LLMConfig(thinking_level="low") # Minimal thinking
@@ -329,8 +330,8 @@ def main():
329330
config_table.add_row("Input Directory", str(input_dir))
330331
config_table.add_row("Output Directory", str(output_dir))
331332
config_table.add_row("Max Chunk Length", f"{args.max_length:,} chars")
332-
if model_option.key == "gemini-flash":
333-
config_table.add_row("Thinking", "Minimal (thinking_level=minimal)")
333+
if model_option.key == "gemini-3.7-flash":
334+
config_table.add_row("Thinking", "Shallowest available (thinking_level=low)")
334335
elif model_option.key == "gemini-pro":
335336
config_table.add_row("Thinking Level", "low")
336337
console.print(config_table)

AI_ocr_correction/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,15 @@ provenance survives.
4242

4343
```bash
4444
# Place ALTO XML files in ALTO/ folder
45-
python 02_correct_alto_xml.py --model gemini-flash
45+
python 02_correct_alto_xml.py --model gemini-3.7-flash
4646
# Corrected files saved to ALTO_Corrected/
4747
```
4848

4949
## Supported Models
5050

5151
| Model | Speed | Best For |
5252
|-------|-------|----------|
53-
| `gemini-flash` | Fast | Bulk processing |
53+
| `gemini-3.7-flash` | Fast | Bulk processing |
5454
| `gemini-pro` | Slower | Complex documents |
5555
| `gpt-5.6-luna` | Fast | General use |
5656
| `gpt-5.6-sol` | Slower | Higher quality |

AI_ocr_extraction/02_gemini_ocr_processor.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@
2121
2222
Model Selection:
2323
- Uses shared LLM provider with three options (all via the Gemini API):
24-
* Gemini Flash — faster, cost-effective, uses MINIMAL thinking
24+
* Gemini 3.7 Flash — faster, cost-effective, uses LOW thinking
25+
(3.7 dropped MINIMAL; LOW is the shallowest rung it has.)
2526
* Gemini Pro — higher quality, uses LOW thinking
2627
* Gemma 4 31B — open-weights flagship, uses MINIMAL thinking
2728
(Gemma 4 accepts only MINIMAL or HIGH; MINIMAL is used for OCR speed.)
@@ -133,14 +134,15 @@ def main():
133134

134135
# OCR runs at the model's default temperature: Google recommends sending none
135136
# for Gemini 3, because a lowered one can make the model loop — on a page scan
136-
# that shows up as the same line repeating until max_output_tokens. Minimal
137-
# thinking for speed. All Gemini 3 / Gemma 4 models use thinking_level (it
138-
# cannot be disabled):
139-
# Gemini Flash: MINIMAL — fastest, sufficient for OCR
140-
# Gemini Pro: LOW — Pro does not accept MINIMAL
141-
# Gemma 4: MINIMAL — only MINIMAL or HIGH accepted; MINIMAL for speed
137+
# that shows up as the same line repeating until max_output_tokens.
138+
#
139+
# Ask for the shallowest thinking there is and let the registry's clamp find
140+
# it: thinking cannot be disabled on any of these models, and which rungs
141+
# exist is per-model and changes between releases (Gemini 3.7 Flash dropped
142+
# MINIMAL; Gemma 4 has only MINIMAL and HIGH). Naming the rung here is how
143+
# this line used to encode "Flash means MINIMAL", which stopped being true.
142144
llm_config = LLMConfig(
143-
thinking_level="LOW" if "pro" in model_option.model.lower() else "MINIMAL",
145+
thinking_level=get_thinking_level(model_option.model, "MINIMAL"),
144146
)
145147

146148
console.print(key_value_table([

0 commit comments

Comments
 (0)