aireview 2.2.0 → 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/README.md +6 -0
- data/lib/aireview/llm_client.rb +35 -8
- data/lib/aireview/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7919de6ffa17aab9f5250ebf07dfec9ce83db9313ef01e6ba93a3b131da927ce
|
|
4
|
+
data.tar.gz: 348cda52953ff2d22ba56fb16b192876249efa30c5ba77f6f0736c1b96e5f569
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 372867e5a1547b4d595312498107dd8c33c6fe6721efb8f4c215850660bcf57706f5a864c9d9b7d46a9d2a1e29fdf18ab31e9ddb77c1859237ddbe5d979b2946
|
|
7
|
+
data.tar.gz: b3b079dbb2b5b92ffe39fc36c6de224d9bf2268e11b353129236e15b5c14689a9067012da053b8415bb53f64b2f52247ef252062831e9e73d789b943a611ed0a
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.2.1
|
|
4
|
+
|
|
5
|
+
- OpenAI reasoning models (`o1`, `o3`, `gpt-5`…) and search models
|
|
6
|
+
(`gpt-4o-search-preview`…) get no temperature: RubyLLM 2 sends it as given
|
|
7
|
+
(1.x replaced it with 1.0 or dropped it), and these models reject any
|
|
8
|
+
other value with a bad request, which failed the run without trying a
|
|
9
|
+
fallback model. The RubyLLM registry says which models take a
|
|
10
|
+
temperature; a model it does not know is judged by its name, as 1.x did.
|
|
11
|
+
`models check` is fixed the same way.
|
|
12
|
+
|
|
3
13
|
## 2.2.0
|
|
4
14
|
|
|
5
15
|
- Jev shadow mode (`llm.jev.shadow`, `LLM_JEV_SHADOW`, key `JEV_API_KEY`):
|
data/README.md
CHANGED
|
@@ -226,6 +226,12 @@ or without auth when that is not set, and needs no key to start. Without an
|
|
|
226
226
|
`api_base` a model goes to its provider's API, or to `LLM_API_BASE` for
|
|
227
227
|
Gemini, OpenAI and OpenRouter as before.
|
|
228
228
|
|
|
229
|
+
A stage's temperature goes only to the models that take one: OpenAI
|
|
230
|
+
reasoning models (`o1`, `o3`, `gpt-5`…) and search models
|
|
231
|
+
(`gpt-4o-search-preview`…) accept no other, so they get none and run with
|
|
232
|
+
their own default. The RubyLLM registry decides, and the name
|
|
233
|
+
for a model it does not know.
|
|
234
|
+
|
|
229
235
|
Instead of an LLM the critic can be Jev (see "Critique engine").
|
|
230
236
|
|
|
231
237
|
### Local Ollama
|
data/lib/aireview/llm_client.rb
CHANGED
|
@@ -17,6 +17,16 @@ module Aireview
|
|
|
17
17
|
end
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
+
# RubyLLM 2 sends the temperature as given, while 1.x replaced it with
|
|
21
|
+
# 1.0 for OpenAI reasoning models (o1, o3, gpt-5…) and dropped it for the
|
|
22
|
+
# search ones (gpt-4o-search-preview…), which take no other: such a
|
|
23
|
+
# request is a BadRequest, fatal for the router, and the stage would not
|
|
24
|
+
# even reach a fallback model. The RubyLLM registry knows which models
|
|
25
|
+
# take a temperature; where it does not (a server of your own,
|
|
26
|
+
# assume_model_exists, search models with an empty flag) the name
|
|
27
|
+
# decides, as in 1.x.
|
|
28
|
+
NO_TEMPERATURE_MODELS = %r{\A(?:openai/)?(?:o\d|gpt-5)|-search}
|
|
29
|
+
|
|
20
30
|
def initialize(config:, logger: Logger.new($stderr))
|
|
21
31
|
@config = config
|
|
22
32
|
@logger = logger
|
|
@@ -26,16 +36,10 @@ module Aireview
|
|
|
26
36
|
# Returns the RubyLLM answer; read it with LlmClient.content. A request
|
|
27
37
|
# error is re-raised as is — LlmFailure classifies it.
|
|
28
38
|
def request(prompt, candidate:, key:, timeout:, key_index: 0)
|
|
29
|
-
load_ruby_llm
|
|
30
39
|
stage = prompt.stage.to_s
|
|
31
40
|
model = candidate.model
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
model: model, provider: candidate.provider)
|
|
35
|
-
chat = configure_reasoning(chat: chat, model: model, provider: candidate.provider)
|
|
36
|
-
.with_temperature(prompt.temperature.to_f)
|
|
37
|
-
.with_schema(prompt.schema)
|
|
38
|
-
chat.with_instructions(prompt.system)
|
|
41
|
+
chat = prepare(prompt, candidate: candidate, key: key, key_index: key_index)
|
|
42
|
+
@logger.info("LLM #{stage} request started (model=#{model}, temperature=#{chat.temperature || 'model default'})")
|
|
39
43
|
response = Timeout.timeout(timeout) { chat.ask(prompt.user) }
|
|
40
44
|
@logger.info("LLM #{stage} request completed (model=#{model}#{token_counts(response)})")
|
|
41
45
|
response
|
|
@@ -44,6 +48,21 @@ module Aireview
|
|
|
44
48
|
raise
|
|
45
49
|
end
|
|
46
50
|
|
|
51
|
+
# The request as it will go, without sending it: the chat with the model,
|
|
52
|
+
# key, schema, instructions and temperature set. chat.render builds it —
|
|
53
|
+
# that is how the specs check the real request without the network.
|
|
54
|
+
def prepare(prompt, candidate:, key:, key_index: 0)
|
|
55
|
+
load_ruby_llm
|
|
56
|
+
stage = prompt.stage.to_s
|
|
57
|
+
chat = build_chat(context: context(stage, candidate, key, key_index), stage: stage,
|
|
58
|
+
model: candidate.model, provider: candidate.provider)
|
|
59
|
+
chat = configure_reasoning(chat: chat, model: candidate.model, provider: candidate.provider)
|
|
60
|
+
.with_temperature(temperature_for(chat, prompt, candidate))
|
|
61
|
+
.with_schema(prompt.schema)
|
|
62
|
+
chat.with_instructions(prompt.system)
|
|
63
|
+
chat
|
|
64
|
+
end
|
|
65
|
+
|
|
47
66
|
# The answer as RubyLLM 1.x gave it under a schema: the parsed JSON when
|
|
48
67
|
# the text is JSON, the text itself otherwise (the pipeline repairs it).
|
|
49
68
|
# RubyLLM 2 always returns the text, and a Hash that breaks the schema
|
|
@@ -82,6 +101,14 @@ module Aireview
|
|
|
82
101
|
raise ConfigError, "Missing dependency: #{e.message}"
|
|
83
102
|
end
|
|
84
103
|
|
|
104
|
+
# The temperature for a model that takes one (see NO_TEMPERATURE_MODELS);
|
|
105
|
+
# nil — its own default, left out of the request.
|
|
106
|
+
def temperature_for(chat, prompt, candidate)
|
|
107
|
+
accepts = chat.model.metadata[:temperature] if chat.model.respond_to?(:metadata)
|
|
108
|
+
accepts = !candidate.model.to_s.match?(NO_TEMPERATURE_MODELS) if accepts.nil?
|
|
109
|
+
accepts ? prompt.temperature.to_f : nil
|
|
110
|
+
end
|
|
111
|
+
|
|
85
112
|
def configure_reasoning(chat:, model:, provider:)
|
|
86
113
|
return chat unless provider == 'ollama' && model.start_with?('gpt-oss:')
|
|
87
114
|
|
data/lib/aireview/version.rb
CHANGED