ace-review 0.53.5 → 0.53.6

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c50e105e74afc8802976a5c869debdd9514996f8c5c48c1ff0cc2266f1a2622f
4
- data.tar.gz: 18512f481aa7315c446b7bfb93d7844dde492f1369b3a33cc34c3edb590bb1d0
3
+ metadata.gz: 647741c9f1b7814b5471a9a07c4f1693ac649f953d3f530bae67516a81cd7f0d
4
+ data.tar.gz: 520c59a0dda292b28d383215606fb7463d64d35f3b3ef0c024428243f2dc0870
5
5
  SHA512:
6
- metadata.gz: 1869bc1bcbe622b59b88189ccbaf09192b183b44aac2a10d926b38befbb58f6e01a54601c218234d0e681532d590a4bf2d531d3ca5f027dda17957f19a046270
7
- data.tar.gz: 68f5a18ae201735fb0577235d1a9bc53e703ea2e44c89e927b053e1048fa1bcfb2782facfed324a612b42cc8a07956fd96a99cc18599ebdd3538c86bcccb7105
6
+ metadata.gz: cb3239ee728103949252327de0a7b97de54ea1747ca3a8c2c2fd1e930281091bcb57ce14de3ca44041bf2d95d14608883b753f4af2b85f2d4d019fd4829bfb32
7
+ data.tar.gz: 892722919f96459f188ddd278f8d42f0538f81297fd9c87729302ca2072f9c62901f58396a7a17a72bb6c677394eb1f341927c2b3a0d53b199450a8dd845d0a1
data/CHANGELOG.md CHANGED
@@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.53.6] - 2026-04-24
11
+
12
+ ### Fixed
13
+ - Sized review prompts and adaptive review execution against the resolved concrete model target so role and alias-based reviews no longer rely on provider-wide context assumptions.
14
+
10
15
  ## [0.53.5] - 2026-04-19
11
16
 
12
17
  ### Technical
@@ -1,161 +1,34 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "ace/llm"
4
+
3
5
  module Ace
4
6
  module Review
5
7
  module Atoms
6
- # Pure function for resolving model context limits
7
- #
8
- # Maps model names to their context window sizes. Handles provider prefixes
9
- # (google:, anthropic:, openai:) and uses pattern matching for model families.
10
- #
11
- # Resolution order:
12
- # 1. ace-llm provider config (context_limit in providers/*.yml)
13
- # 2. Hardcoded pattern matching (MODEL_LIMITS)
14
- # 3. Conservative default for unknown models
15
- #
16
- # @example Basic usage
17
- # ContextLimitResolver.resolve("google:gemini-2.5-pro")
18
- # #=> 1_000_000
19
- #
20
- # @example Without provider prefix
21
- # ContextLimitResolver.resolve("claude-3-sonnet")
22
- # #=> 200_000
8
+ # Review-local wrapper around ace-llm's resolved model limit lookup.
23
9
  module ContextLimitResolver
24
- # Conservative default for unknown models
25
- DEFAULT_LIMIT = 200_000
26
-
27
- # Model patterns and their context limits (fallback when ace-llm config unavailable)
28
- # Order matters - first match wins
29
- # Patterns use regex for flexible matching
30
- MODEL_LIMITS = [
31
- # Gemini models
32
- {pattern: /gemini-1\.5-pro/i, limit: 2_000_000},
33
- {pattern: /gemini-1\.5-flash/i, limit: 1_000_000},
34
- {pattern: /gemini-2\.5-pro/i, limit: 1_000_000},
35
- {pattern: /gemini-2\.5-flash/i, limit: 1_000_000},
36
- {pattern: /gemini-2\.0/i, limit: 1_000_000},
37
- # Fallback for any other gemini model
38
- {pattern: /gemini/i, limit: 1_000_000},
39
-
40
- # Claude models (all variants: opus, sonnet, haiku)
41
- {pattern: /claude.*opus/i, limit: 1_000_000},
42
- {pattern: /claude.*sonnet/i, limit: 1_000_000},
43
- {pattern: /claude.*haiku/i, limit: 1_000_000},
44
- # Fallback for any other claude model
45
- {pattern: /claude/i, limit: 1_000_000},
10
+ DEFAULT_LIMIT = Ace::LLM::Molecules::ModelLimitResolver::DEFAULT_CONTEXT_LIMIT
46
11
 
47
- # OpenAI models
48
- {pattern: /gpt-5\.\d/i, limit: 1_050_000},
49
- {pattern: /o4-/i, limit: 1_050_000},
50
- {pattern: /gpt-4o/i, limit: 128_000},
51
- {pattern: /gpt-4-turbo/i, limit: 128_000},
52
- {pattern: /gpt-4-32k/i, limit: 32_768},
53
- {pattern: /gpt-4-\d+-preview/i, limit: 128_000}, # gpt-4-1106-preview, gpt-4-0125-preview
54
- {pattern: /gpt-4-\d+$/i, limit: 8_192}, # legacy gpt-4-0613, etc.
55
- {pattern: /gpt-4$/i, limit: 8_192}, # base gpt-4 model
56
- {pattern: /o1-/i, limit: 200_000},
57
- {pattern: /o3-/i, limit: 200_000}
58
- ].freeze
59
-
60
- # Resolve context limit for a model
61
- #
62
- # Resolution order:
63
- # 1. ace-llm provider config (if available and provider specified)
64
- # 2. Hardcoded pattern matching
65
- # 3. Default limit
66
- #
67
- # @param model_name [String, nil] Model identifier, optionally with provider prefix
68
- # @return [Integer] Context limit in tokens
69
- #
70
- # @example With provider prefix
71
- # ContextLimitResolver.resolve("google:gemini-2.5-pro")
72
- # #=> 1_000_000
73
- #
74
- # @example Without provider prefix
75
- # ContextLimitResolver.resolve("claude-3-opus")
76
- # #=> 200_000
77
- #
78
- # @example Unknown model
79
- # ContextLimitResolver.resolve("unknown-model")
80
- # #=> 128_000
81
12
  def self.resolve(model_name)
82
- return DEFAULT_LIMIT if model_name.nil? || model_name.empty?
83
-
84
- # Try to get limit from ace-llm provider config first
85
- limit = load_from_ace_llm(model_name)
86
- return limit if limit
87
-
88
- # Fall back to hardcoded pattern matching
89
- normalized = strip_provider_prefix(model_name)
90
- match = MODEL_LIMITS.find { |entry| normalized.match?(entry[:pattern]) }
91
- match ? match[:limit] : DEFAULT_LIMIT
13
+ resolve_details(model_name).context_limit
92
14
  end
93
15
 
94
- # Get the default limit for unknown models
95
- #
96
- # @return [Integer] Default context limit
97
- def self.default_limit
98
- DEFAULT_LIMIT
99
- end
100
-
101
- # Load context limit from ace-llm provider configuration
102
- #
103
- # @param model_name [String] Model identifier with provider prefix
104
- # @return [Integer, nil] Context limit from config, or nil if not found
105
- def self.load_from_ace_llm(model_name)
106
- # Extract provider prefix (e.g., "google" from "google:gemini-2.5-pro")
107
- return nil unless model_name.include?(":")
108
-
109
- provider = model_name.split(":").first
110
- return nil if provider.nil? || provider.empty?
111
-
112
- # Try to load provider config via ace-llm
113
- config = load_provider_config(provider)
114
- return nil unless config
115
-
116
- # Get context_limit from provider config
117
- limit = config["context_limit"]
118
- limit.is_a?(Integer) ? limit : nil
16
+ def self.resolve_details(model_name)
17
+ Ace::LLM::Molecules::ModelLimitResolver.resolve(model_name)
119
18
  rescue
120
- nil # Fall back to hardcoded on any error
121
- end
122
- private_class_method :load_from_ace_llm
123
-
124
- # Load provider configuration from ace-llm
125
- #
126
- # @param provider [String] Provider name (e.g., "google", "anthropic")
127
- # @return [Hash, nil] Provider config hash or nil
128
- def self.load_provider_config(provider)
129
- # Try to use ace-llm's config loader if available
130
- return nil unless defined?(Ace::LLM::Molecules::ConfigLoader)
131
-
132
- resolver = Ace::Support::Config.create(
133
- config_dir: ".ace",
134
- defaults_dir: ".ace-defaults",
135
- gem_path: Ace::LLM::Molecules::ConfigLoader.gem_root
19
+ Ace::LLM::Molecules::ModelLimitResolver::ResolveResult.new(
20
+ provider: nil,
21
+ model: nil,
22
+ context_limit: DEFAULT_LIMIT,
23
+ output_limit: nil,
24
+ source: :fallback,
25
+ original_target: model_name.to_s
136
26
  )
137
-
138
- config = resolver.resolve_namespace("llm", filename: "providers/#{provider}")
139
- config.to_h
140
- rescue
141
- nil
142
27
  end
143
- private_class_method :load_provider_config
144
28
 
145
- # Strip provider prefix from model name
146
- #
147
- # @param model_name [String] Model identifier
148
- # @return [String] Model name without provider prefix
149
- #
150
- # @example
151
- # strip_provider_prefix("google:gemini-2.5-pro")
152
- # #=> "gemini-2.5-pro"
153
- def self.strip_provider_prefix(model_name)
154
- # Common provider prefixes
155
- model_name.sub(/\A(google|anthropic|openai|codex|cli):/, "")
29
+ def self.default_limit
30
+ DEFAULT_LIMIT
156
31
  end
157
-
158
- private_class_method :strip_provider_prefix
159
32
  end
160
33
  end
161
34
  end
@@ -67,12 +67,19 @@ module Ace
67
67
  total_chars = (system_prompt&.length || 0) + (user_prompt&.length || 0)
68
68
  estimated_tokens = total_chars / 4 # Rough estimate: 4 chars per token
69
69
 
70
- context_limit = Ace::Review::Atoms::ContextLimitResolver.resolve(model)
70
+ limit_details = Ace::Review::Atoms::ContextLimitResolver.resolve_details(model)
71
+ context_limit = limit_details.context_limit
71
72
  threshold = (context_limit * PROMPT_SIZE_WARNING_RATIO).to_i
72
73
  return unless estimated_tokens > threshold
73
74
 
75
+ display_model = if limit_details.full_model && limit_details.full_model != model
76
+ "#{model} -> #{limit_details.full_model}"
77
+ else
78
+ model
79
+ end
80
+
74
81
  warn "Warning: Prompt size (~#{estimated_tokens.to_s.gsub(/(\d)(?=(\d{3})+(?!\d))/, '\\1,')} tokens) " \
75
- "may exceed #{model} context limit (#{context_limit.to_s.gsub(/(\d)(?=(\d{3})+(?!\d))/, '\\1,')} tokens)"
82
+ "may exceed #{display_model} context limit (#{context_limit.to_s.gsub(/(\d)(?=(\d{3})+(?!\d))/, '\\1,')} tokens)"
76
83
  end
77
84
 
78
85
  # Check if Ruby API is available
@@ -14,8 +14,8 @@ module Ace
14
14
  # Default timeout for LLM queries (5 minutes)
15
15
  DEFAULT_LLM_TIMEOUT = 300
16
16
 
17
- # Warning threshold: 80% of typical 1M context window
18
- PROMPT_SIZE_WARNING_THRESHOLD = 800_000
17
+ # Warning threshold: 80% of the smallest resolved context window in the batch
18
+ PROMPT_SIZE_WARNING_RATIO = LlmExecutor::PROMPT_SIZE_WARNING_RATIO
19
19
 
20
20
  def initialize(max_concurrent: nil, llm_timeout: nil)
21
21
  # Read from config, fallback to default of 3, clamp to minimum 1
@@ -266,7 +266,14 @@ module Ace
266
266
  total_chars = (system_prompt&.length || 0) + (user_prompt&.length || 0)
267
267
  estimated_tokens = total_chars / 4 # Rough estimate: 4 chars per token
268
268
 
269
- return unless estimated_tokens > PROMPT_SIZE_WARNING_THRESHOLD
269
+ resolved_limits = models.map do |model|
270
+ Atoms::ContextLimitResolver.resolve_details(model)
271
+ end
272
+ min_context_limit = resolved_limits.map(&:context_limit).compact.min
273
+ return if min_context_limit.nil?
274
+
275
+ threshold = (min_context_limit * PROMPT_SIZE_WARNING_RATIO).to_i
276
+ return unless estimated_tokens > threshold
270
277
 
271
278
  model_list = models.join(", ")
272
279
  warn "Warning: Prompt size (~#{estimated_tokens.to_s.gsub(/(\d)(?=(\d{3})+(?!\d))/, '\\1,')} tokens) " \
@@ -70,7 +70,7 @@ module Ace
70
70
  explicit_limit = context[:model_context_limit] || context["model_context_limit"]
71
71
 
72
72
  # Resolve model context limit
73
- model_limit = explicit_limit || Atoms::ContextLimitResolver.resolve(model)
73
+ model_limit = explicit_limit || Atoms::ContextLimitResolver.resolve_details(model).context_limit
74
74
 
75
75
  # Select and delegate to appropriate strategy
76
76
  selected = select_strategy(subject, model_limit, model)
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Ace
4
4
  module Review
5
- VERSION = '0.53.5'
5
+ VERSION = '0.53.6'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ace-review
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.53.5
4
+ version: 0.53.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michal Czyz
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2026-04-20 00:00:00.000000000 Z
10
+ date: 2026-04-27 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: ace-support-cli