Chatモデル作成時にModalitiesとPricingをハッシュへシリアライズ

crmne/ruby_llm

find_or_create_model! が生成するチャットモデルの modalitiespricing を、永続化直前に ハッシュ に変換して保存するよう変更されました。これにより JSONB カラムの形がレジストリ同期と同一になり、価格計算等の downstream ロジックが期待通りに動作します。

背景

find_or_create_model! は LLM から取得した Model::Info をそのまま ActiveRecord の JSONB カラムに書き込んでいました。Model::Pricing オブジェクトは as_json がデフォルトでインスタンス変数(@data, @values)を出力するため、DB には "data""values" といった余計なキーが混入していました。この不整合により、例えば model.pricing.dig("text_tokens", "standard", "input_per_million")nil になるなど、コスト計算が正しく行えないケースが報告されました(Issue #784)。

技術的な変更

コード変更概要

resolve_model_from_strings 内で属性を設定する箇所が、to_h 呼び出しを追加するだけにシンプルに変更されました。具体的な差分は以下の通りです。

変更前:

          m.modalities = model_info.modalities || {}
          m.pricing = model_info.pricing || {}

変更後:

          m.modalities = model_info.modalities.to_h
          m.pricing = model_info.pricing.to_h

この変更により、ModalitiesPricing のオブジェクトはそれぞれ内部実装に依存しない 純粋なハッシュ へ変換され、ActiveRecord がそれをそのまま JSON にシリアライズします。結果として DB に保存される JSON の構造は acts_as_model のレジストリ同期が生成する形と一致します。

テストでの検証

新たに追加されたテストケースは、ハッシュ変換後の永続化が正しく行われることを検証しています。model_info に意図的な modalitiespricing を設定し、chat_class.create! を実行した後に chat.model.reload で取得したレコードの modalitiespricing が期待通りのハッシュ構造になっていることを expect(...).to eq で確認します。このテストは変更前に失敗していたシナリオをカバーし、回帰防止にも寄与します。

設計判断

ハッシュ化のタイミング をモデル作成直前に持ってくる選択は、既存の公開 API を一切変更せずに問題を解決できる点で妥当性があります。Model::Infomodalitiespricing は本来 値オブジェクト として扱われ、外部からは .to_h が提供されているため、呼び出し側で変換するだけで十分です。代替案として新しい属性キー(例: modalities_hash)を導入する案も検討されましたが、キーの増加は既存コードの更新を必要とし、後方互換性リスクが高まります。今回の実装は最小限の差分で一貫性を確保し、他コンポーネント(from_llm_attributes など)との挙動差異を排除しています。

まとめ

find_or_create_model!modalitiespricing をハッシュにシリアライズして永続化するよう改修したことで、JSONB カラムの構造がレジストリ同期と揃い、価格情報の取得や計算が期待通りに機能するようになりました。変更は公開 API に影響せず、テストによって正確性が保証されているため、既存ユーザーへのリスクは最小限です。

記事メタデータ

Generated by:
gpt-oss-120b for DiffDaily
LLM Trace:
568a4bf2

この記事はAIによって自動生成されています。内容の正確性については、必ずソースコードやPRを確認してください。

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
gpt-oss-120b for DiffDaily

Review Criteria:

記事構成 ✓ PASS

Title, Context, Technical Detailの存在と明確さ

リード文→背景→技術的な変更→設計判断→まとめという『総論→各論→結論』の構成が明確に存在し、必須要素がすべて揃っています。

カスタムMarkdown構文 ⚠ WARNING

シンタックスハイライト・GitHubリンク記法の正確性

コードブロックは正しい `ruby:ファイルパス` 形式で記載されていますが、Issue #784 がリンク化されていません([#784](URL) 形式が必要)。その他のGitHubリンクは正しく記述されています。

対象読者への適合性 ✓ PASS

エンジニア向けの適切な技術レベルと表現

エンジニア向けの技術的記述に留まり、初心者向けの冗長な解説はありません。

パラグラフ・ライティング ✓ PASS

トピックセンテンス・1段落1トピック・段落長

各セクションは総論・各論・結論のパラグラフ構成があり、トピックセンテンスで始まり、1段落1トピック、長さも適切です。段落間は空行で区切られています。

Diff内容との照合 ✓ PASS

コードブロックとDiff内容の一致

記事中のコードブロックは Diff の変更内容と完全に一致しており、ファイル名・変更行も正確です。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

使用されている専門用語は PR と合致しており、誤用は見られません。

説明の技術的正確性 ✓ PASS

技術的主張の正確性と論理性

技術的説明は PR の目的(ハッシュ化による JSONB 整合性の確保)と合致し、根拠が示されています。

事実の突合 ✓ PASS

PR情報による主張の裏付け(ハルシネーション検出)

主張は PR のタイトル・説明・Diff で裏付けられており、未根拠の推測や外部知識の付加はありません。

数値・固有名詞の確認 ✓ PASS

PR番号・コミットID・バージョン等の正確性

PR 番号 #796 と Issue 番号 #784 が正しく記載されています。数値的な誤りはありません。

タイトル・説明との一致 ✓ PASS

記事タイトル・説明とPR内容の一致

記事タイトルは PR の内容(チャットモデル作成時のハッシュシリアライズ)を適切に表現しています。

外部知識の正確性 ✓ PASS

PRに記載のない外部知識(LTS、サポート状況など)の不使用

バージョンサポートやリリース日程等、PR には記載されていない外部情報は含まれていません。

時間表現の正確性 ✓ PASS

時間表現がPR情報と一致しているか

時間表現の歪曲はなく、PR の記述と一致しています。