Release v1.2.2 — Model metadata overrides fix + Agent guidelines consolidation#26
Conversation
* fix: apply model metadata overrides to provider models * test: isolate auth store XDG data home * fix: treat string metadata matches as literals * refactor: centralize metadata extraction keys * fix: refresh legacy generated provider models --------- Co-authored-by: Sebastian Rumpf <alp4d0g007@googlemail.com>
- Rewrite AGENTS.md by cherry-picking the best of AGENTS.md and CLAUDE.md: - Keep generic Agent Guidelines framing and all code style rules - Add Architecture section (dual entry points, core modules, fetch interceptor, caching) - Expand Common Commands (test runner, check:exports, single-file typecheck) - Add Testing, Important Configuration, and full Release Process sections - Delete CLAUDE.md and replace with symlink to AGENTS.md so both Claude Code and other agents read from the same canonical source.
Brings in PR #25 fixes (model metadata overrides to provider models) alongside the local AGENTS.md consolidation and CLAUDE.md symlink.
Code Review SummaryVerdict: No Issues Found | Recommendation: Merge Oh wait, this PR is actually clean. I need to sit down. I had my flamethrower warmed up and everything. 📊 Overall: Like finding a unicorn in production — I didn't think clean PRs existed anymore, but here we are. Files Reviewed (1 file)
Incremental Review NotesChanges since last review (commit The single incremental commit (
This directly addresses the previous review comment about performance (redundant Previously noted issue (getApiMode warn() at line 269) remains unchanged but is outside this diff and noted as pre-existing in the prior review. Reviewed by: Kilo (kilo-auto/free) Reviewed by laguna-m.1-20260312:free · 2,093,213 tokens |
There was a problem hiding this comment.
Code Review
This pull request consolidates agent guidelines into AGENTS.md, documents the release process, and fixes an issue where model metadata overrides were not correctly applied to provider models. It introduces a mechanism to track raw user-authored metadata separately from generated metadata using symbols and internal options, ensuring user overrides persist across plugin lifecycle hooks. New logic in src/plugin.ts allows for complex metadata overrides using literal IDs or regex patterns, including the ability to add missing models. Feedback highlights performance concerns in the array-based override logic due to redundant calculations and regex re-creation within loops. It also identifies a logging issue with serialized regex objects and recommends pre-processing configuration blocks. Additionally, a reminder was provided to ensure model capability flags are merged using a logical OR when grouping variants.
| if (Array.isArray(userConfig)) { | ||
| const validBlocks = userConfig.filter((block) => { | ||
| const validation = isValidModelMetadata(block); | ||
| if (!validation.valid) { | ||
| warn(`Invalid metadata block for match "${sanitizeForLog(String(block.match))}" (field: ${sanitizeForLog(validation.field ?? '')}), skipping`); | ||
| return false; | ||
| } | ||
| return true; | ||
| }); | ||
|
|
||
| const modelsWithOverrides = models.map((model) => { | ||
| const canonicalId = resolveProviderAliasForMetadata(model.id); | ||
| const block = validBlocks.find((candidate) => | ||
| metadataBlockMatches(candidate.match, model.id, canonicalId), | ||
| ); | ||
| if (!block) return model; | ||
|
|
||
| return { | ||
| ...model, | ||
| ...extractModelMetadata(block), | ||
| }; | ||
| }); | ||
|
|
||
| const existingModels = modelsWithOverrides.map((model) => ({ | ||
| id: model.id, | ||
| canonicalId: resolveProviderAliasForMetadata(model.id), | ||
| })); | ||
| const missingModels: OmniRouteModel[] = []; | ||
| for (const block of validBlocks) { | ||
| if (block.addIfMissing !== true || typeof block.match !== 'string') continue; | ||
|
|
||
| const id = resolveProviderAliasForMetadata(block.match); | ||
| const alreadyExists = existingModels.some((model) => | ||
| metadataBlockMatches(block.match, model.id, model.canonicalId), | ||
| ) || missingModels.some((model) => model.id === id); | ||
| if (alreadyExists) continue; | ||
|
|
||
| missingModels.push({ | ||
| id, | ||
| name: block.name ?? id, | ||
| ...extractModelMetadata(block), | ||
| }); | ||
| } | ||
|
|
||
| return [...modelsWithOverrides, ...missingModels]; | ||
| } |
There was a problem hiding this comment.
The array-based override logic performs redundant calculations inside the models.map loop. Specifically, resolveProviderAliasForMetadata and coerceRegExp are called for every model-block pair (O(N * M)). If the configuration was serialized (e.g., during an OpenCode lifecycle hook), coerceRegExp will re-create RegExp objects on every call. Additionally, String(block.match) at line 538 will log "[object Object]" for serialized regex objects.
Consider pre-processing userConfig into a list of processedBlocks that include pre-compiled regexps and pre-calculated canonical match strings. This will improve performance, which is important as this logic runs in the provider.models hook. Furthermore, ensure that when grouping model variants under a base model, all capability flags (such as vision, tools, streaming, temperature, and attachment support) are merged using a logical OR (union) so the base model accurately reflects the collective capabilities available across its variants.
References
- When grouping model variants under a base model, ensure that all capability flags (such as vision, tools, streaming, temperature, and attachment support) are merged using a logical OR (union) so the base model accurately reflects the collective capabilities available across its variants.
Fixes pre-existing indentation issue flagged by kilo-code-bot review. No functional change.
|
Fixed the pre-existing indentation issue in flagged by @kilo-code-bot. The call was over-indented (4 spaces instead of 2). No functional change — purely formatting. Commit: |
…ulations Addresses gemini-code-assist review feedback on PR #26: - Pre-process array-based metadata blocks once: canonicalize string matches, compile regexes, and extract metadata upfront. - Replaces O(N*M) redundant resolveProviderAliasForMetadata + coerceRegExp calls inside per-model loops with a single pass over config blocks. - Adds processedBlockMatches() helper that uses pre-computed canonicalMatch instead of re-resolving aliases on every comparison. - No functional change — all 66 tests pass.
|
@gemini-code-assist Thanks for the review! I've addressed the performance concern in the array-based override logic (lines 534–579): What changed:
Result: The inner loops are now simple lookups/comparisons with no redundant calculations. All 66 tests pass. Commit: |
|
@Alph4d0g Thank you for the detailed breakdown of the performance optimizations. Moving the pre-processing logic outside of the per-model loops is a significant improvement, and the use of pre-compiled Regex and canonicalized string lookups is exactly the right approach for this type of metadata enrichment. The added regression tests also provide great coverage for these changes. The implementation looks solid and addresses the concerns raised. Great work on this PR. |
Summary
This release includes the model metadata override fix from PR #25 and consolidates the project's agent guidelines documentation.
Changes
Fixes
Documentation
Version Bump
Files Changed
Checklist
✔ warn() writes to log file with correct format (54.280125ms)
✔ debug() writes when OMNIROUTE_DEBUG=1 (52.285042ms)
✔ debug() does not write when OMNIROUTE_DEBUG is not set (0.62025ms)
✔ debug() does not write when OMNIROUTE_DEBUG is "true" (0.399916ms)
✔ debug() does not write when OMNIROUTE_DEBUG is "0" (0.386333ms)
✔ warn() always writes regardless of OMNIROUTE_DEBUG (51.083875ms)
✔ logger handles missing log directory gracefully (0.47875ms)
✔ logger handles log file rotation (103.131833ms)
✔ logger re-scans when no log file exists at module load (51.29625ms)
✔ logger silently skips on non-ENOENT write errors (0.980125ms)
✔ logger silently skips on unreadable log directory (0.832417ms)
✔ logger excludes directories with .log suffix (51.553334ms)
✔ logger uses alphabetical tie-breaker for identical mtime (67.687375ms)
✔ fresh cache hit - no second network call (25.147208ms)
✔ timeout then success - retries and returns data (251.851583ms)
✔ retryable HTTP failure then success - retries 503 (253.456875ms)
✔ all attempts fail with stale cache available - returns stale data (756.827166ms)
✔ all attempts fail with no cache - returns null (754.300708ms)
✔ non-retryable HTTP failure - fail fast on 404 (1.678917ms)
✔ invalid response structure with stale cache - returns stale data (1.785667ms)
✔ invalid provider structure with no cache - returns null (1.151834ms)
✔ getModelsDevIndex integrates with retry and cache (254.006709ms)
✔ fetchModels caches successful responses (25.160917ms)
✔ refreshModels forces refetch (0.8555ms)
✔ fetchModels falls back to defaults when response shape is invalid (0.272542ms)
✔ calculateLowestCommonCapabilities ignores missing attachment metadata (0.1155ms)
✔ calculateLowestCommonCapabilities respects explicit attachment false (0.087375ms)
✔ fetchModels uses different cache for different modelsDev configs (0.540083ms)
✔ calculateLowestCommonCapabilities produces identical output for single model and combo-with-self (0.094333ms)
✔ calculateLowestCommonCapabilities handles mixed defined and undefined temperature (0.053666ms)
✔ calculateLowestCommonCapabilities handles explicit temperature false overriding true (0.054791ms)
✔ calculateLowestCommonCapabilities handles all three capabilities together (0.073708ms)
✔ calculateLowestCommonCapabilities handles single model with undefined temperature (0.042792ms)
✔ variant suffix stripping works with alias resolution end-to-end (0.205708ms)
✔ subscription provider fallback enriches from public provider (0.073375ms)
✔ groupVariantModels merges capability flags from all variants into synthetic base (0.076ms)
✔ normalizeModel reads snake_case fields (0.278ms)
✔ normalizeModel prefers camelCase over snake_case (0.231458ms)
✔ deduplication removes alias when canonical exists (0.231834ms)
✔ deduplication keeps alias when canonical is missing (0.218333ms)
✔ config hook applies defaults and normalized apiMode (228.582292ms)
✔ loader injects auth headers only for OmniRoute URLs (1.33375ms)
✔ auth loader applies user modelMetadata override to provider models (0.49375ms)
✔ gemini tool schema payload is sanitized before forwarding (0.580584ms)
✔ non-gemini payload keeps original tool schema fields (0.343167ms)
✔ gemini schema sanitization applies to responses endpoint request objects (759.074416ms)
✔ provider hook fetches models when auth is available via context (1.897583ms)
✔ provider hook applies modelMetadata overrides before converting models (1.238333ms)
✔ provider hook applies array literal alias block to canonical fetched model (1.49275ms)
✔ provider hook treats string metadata match as a literal model id (1.158625ms)
✔ provider hook addIfMissing array block creates canonical missing model (1.188333ms)
✔ provider hook ignores generated modelMetadata from config hook (12.51975ms)
✔ provider hook uses raw user modelMetadata after config hook generated metadata (5.505042ms)
✔ provider hook uses RegExp raw modelMetadata after config hook JSON clone (3.841375ms)
✔ auth loader uses raw user modelMetadata after config hook generated metadata (3.553542ms)
✔ provider hook ignores stale provider.models and returns defaults when no auth available (0.22875ms)
✔ provider hook returns defaults when fetch fails (fetchModels handles errors) (0.259916ms)
✔ config hook eagerly fetches models when auth is available (2.565792ms)
✔ config hook refreshes plugin-generated models on second run (8.791625ms)
✔ config hook refreshes legacy generated provider models without marker (2.5305ms)
✔ config hook preserves explicit user provider models (2.399625ms)
✔ config hook preserves user modelMetadata object overrides (7.972458ms)
✔ config hook preserves user modelMetadata match blocks (2.044875ms)
✔ config hook respects explicit attachment false for vision models (2.769541ms)
✔ provider hook groups variant models under base model (0.503875ms)
✔ provider hook creates synthetic base model when only variants are returned (0.325875ms)
ℹ tests 66
ℹ suites 0
ℹ pass 66
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 2387.720333)
Post-Merge Steps
After merging this PR: