/** * WPML compatibility functions * * @global array $duplicated_posts Array to store the posts being duplicated. * * @package Yoast\WP\Duplicate_Post * @since 3.2 */ add_action( 'admin_init', 'duplicate_post_wpml_init' ); /** * Add handlers for WPML compatibility. */ function duplicate_post_wpml_init() { if ( defined( 'ICL_SITEPRESS_VERSION' ) ) { add_action( 'dp_duplicate_page', 'duplicate_post_wpml_copy_translations', 10, 3 ); add_action( 'dp_duplicate_post', 'duplicate_post_wpml_copy_translations', 10, 3 ); add_action( 'shutdown', 'duplicate_wpml_string_packages', 11 ); } } global $duplicated_posts; // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals -- Reason: Renaming a global variable is a BC break. $duplicated_posts = []; /** * Copy post translations. * * @global SitePress $sitepress Instance of the Main WPML class. * @global array $duplicated_posts Array of duplicated posts. * * @param int $post_id ID of the copy. * @param WP_Post $post Original post object. * @param string $status Status of the new post. */ function duplicate_post_wpml_copy_translations( $post_id, $post, $status = '' ) { global $sitepress; global $duplicated_posts; remove_action( 'dp_duplicate_page', 'duplicate_post_wpml_copy_translations', 10 ); remove_action( 'dp_duplicate_post', 'duplicate_post_wpml_copy_translations', 10 ); $current_language = $sitepress->get_current_language(); $trid = $sitepress->get_element_trid( $post->ID ); if ( ! empty( $trid ) ) { $translations = $sitepress->get_element_translations( $trid ); $new_trid = $sitepress->get_element_trid( $post_id ); foreach ( $translations as $code => $details ) { if ( $code !== $current_language ) { if ( $details->element_id ) { $translation = get_post( $details->element_id ); if ( ! $translation ) { continue; } $new_post_id = duplicate_post_create_duplicate( $translation, $status ); if ( ! is_wp_error( $new_post_id ) ) { $sitepress->set_element_language_details( $new_post_id, 'post_' . $translation->post_type, $new_trid, $code, $current_language ); } } } } // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals -- Reason: see above. $duplicated_posts[ $post->ID ] = $post_id; } } /** * Duplicate string packages. * * @global array() $duplicated_posts Array of duplicated posts. */ function duplicate_wpml_string_packages() { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals -- Reason: renaming the function would be a BC-break. global $duplicated_posts; foreach ( $duplicated_posts as $original_post_id => $duplicate_post_id ) { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals -- Reason: using WPML native filter. $original_string_packages = apply_filters( 'wpml_st_get_post_string_packages', false, $original_post_id ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals -- Reason: using WPML native filter. $new_string_packages = apply_filters( 'wpml_st_get_post_string_packages', false, $duplicate_post_id ); if ( is_array( $original_string_packages ) ) { foreach ( $original_string_packages as $original_string_package ) { $translated_original_strings = $original_string_package->get_translated_strings( [] ); foreach ( $new_string_packages as $new_string_package ) { $cache = new WPML_WP_Cache( 'WPML_Package' ); $cache->flush_group_cache(); $new_strings = $new_string_package->get_package_strings(); foreach ( $new_strings as $new_string ) { if ( isset( $translated_original_strings[ $new_string->name ] ) ) { foreach ( $translated_original_strings[ $new_string->name ] as $language => $translated_string ) { do_action( // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals -- Reason: using WPML native filter. 'wpml_add_string_translation', $new_string->id, $language, $translated_string['value'], $translated_string['status'] ); } } } } } } } } dev-portal パラメタ受領追加開発 – Raqqa

dev-portal パラメタ受領追加開発

Phase 7A:新規作成ファイル(この構成に合わせた案)

既存の “routers / schemas / services” 分割に合わせます。

  1. app/routers/sql.py
  • POST /sql/execute_params を定義
  1. app/schemas/sql_execute_params.py
  • Request/Response(SqlResult含む)を定義
  1. app/services/sql_execute_params.py
  • パイプライン本体(Chroma検索 → LLM → Guard → dry_runレスポンス生成)
  1. app/services/sql_guard.py
  • nlq-dev の sql_guard.py をコピー移植(Phase 7A時点では prepare() が使えればOK)

※補足:すでに services/analytics/sql_validator.py があるので、将来的に統合もできますが、Phase7Aは「移植して確実に動かす」が最短です。


Phase 7A:既存修正が必要なファイル

  1. app/main.py
  • routers/sql.py を include_router する
  1. app/openapi/openapi.yaml
  • /sql/execute_params を追記(ここが重要。contract test 回避)
  1. app/routers/__init__.py(必要な場合のみ)
  • ルータをまとめてexportしている運用なら追加
    ※ main.py が個別 import なら不要
  1. app/schemas/__init__.py(必要な場合のみ)
  • スキーマを集約exportしている運用なら追加
    ※ 直接importなら不要
  1. app/config.py(Phase7Aでは “必要なら”)
  • 既存の services/analytics/llm_sql.py設定値を config.Settings から読む作りなら、SQL生成用の model 名や top_k default を追加する可能性あり
  • ただし Phase7A は dry_run のため、最小は「既存設定の流用」で済むケースが多いです

Phase 7A: Endpoint追加+dry_run完了(最小で確実)

目的:nlq-dev から呼べて、SQL生成結果(安全化後のSQL)が返る
Dev-Portal

  • POST /sql/execute_params
  • Chroma検索 → LLM SQL生成 → sql_guard.prepare()
  • options.dry_run=true のとき executed_sqlのみ返す(rows/columns空、row_count=0)
    DoD
  • curlで dry_run 200
  • executed_sql が guard 後の final_sql

Phase 7B: non-dry-runでOdoo DB実行+返却(動く最短)

目的:実行して結果(columns/rows/row_count)を返す
Dev-Portal

  • Odoo DB接続(host=postgres, db=odoo, user=odoo…)
  • SET LOCAL statement_timeout 等を必ず入れてから実行
  • rows/columns 返却(columns + rows 形式)
    DoD
  • 軽いクエリで rows が返る
  • timeout を超えるクエリがちゃんと止まる

Phase 7C: 運用の地雷を踏まない仕上げ(ここで“安全にする”)

目的:nlq-dev 側で落ちない・暴走しない
Dev-Portal

  • 結果の JSONシリアライズ正規化(datetime/Decimal/UUID/JSONBなど)
  • max_rows の上限制御(LIMIT矯正)
  • エラー形式の統一(Problem+JSON等)
  • 任意で sql_hash / guards_applied 追加
    DoD
  • nlq-dev が例外なく受け取れる
  • 危険SQL(DDL/DML等)が確実にブロックされる

既存で修正が必要なファイル(Phase 7A)

  1. api/app/main.py
    • include_router(sql_router, prefix="") を追加
    • tags の整備(必要なら)
  2. api/app/config.py
    • Settings 追加(Phase 7A で最低限必要なもの)
      • LLM用(既に OpenAI は使っているはずなので不足分だけ)
        • SQL_BUILD_MODEL(例: gpt-4o-mini 等)※既存の translate用モデルとは分けてもOK
      • Chroma検索用
        • CHROMA_URL(既存)
        • CHROMA_COLLECTION_FIELD_JA=portal_field_ja
        • CHROMA_COLLECTION_VIEW_COMMON_JA=portal_view_common_ja
        • CHROMA_TOP_K_DEFAULT(任意)
    • ※Odoo DB 接続設定は Phase 7B で必須。7Aでは不要。
  3. 既存の api/app/services/chroma_client.py / services/*(必要なら)
    • すでに Chroma 検索があるなら流用して 検索関数を1つ足すだけで済む可能性あり
    • 例:search_docs(query, collections=[...], top_k=...)
  4. 既存の OpenAI ラッパ(もしある場合)
    • services/translate.py 等しかないなら、SQL生成用の 軽い呼び出し関数を追加
    • 例:services/llm_client.py を新規に切っても良い(ただし7Aは最小でOK)


Comments

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です