Python SDKおよびREST APIを使用したモデルの検出

OCI Generative AIモデル・カタログを確認し、アプリケーションのモデルをフィルタリングし、署名されたPython SDKおよびRESTリクエストでモデル・メタデータを検査します。

概要

モデル検出APIは、モデル識別子、機能、API互換性、構成パラメータおよびリージョンの可用性をまとめます。このメタデータを使用して、推論リクエストを記述する前に、チャット・アシスタント、検索アプリケーションまたはマルチモーダル・ワークフローのモデルを選択します。

アプリケーションはカタログを問い合せて、モデル選択インタフェースを構築し、使用可能な選択肢をリフレッシュできます。リージョナル・ライフサイクル情報は、アップグレードを計画し、レポートされた置換を評価するのに役立ちます。

メタデータ報告内容使用方法
アイデンティティおよびプロバイダモデル識別子とベンダー情報。プロバイダを検索し、正確なモデルIDをコピーします。
タスクとモーダリティモデル機能およびサポートされている入出力タイプ。チャット、埋込み、再ランキングまたはマルチモーダル・ワークフローの候補者を特定します。
APIの互換性モデルについてレポートされた推論API機能。モデルがサポートするAPIを選択します。
リージョン別の可用性レルム、リージョン、オンデマンドまたは専用サービング・モード。配置場所と提供モードの候補を検索します。
ホストまたはプロキシ・アクセスモデルのアクセス分類。適切なデプロイメントおよびデータ処理ドキュメントを確認します。
パラメータレポートされる名前、タイプ、範囲、デフォルトおよび説明。構成フォームを構築し、返されたメタデータに対して設定を確認します。
ライフサイクルリージョンおよびサービング・モード別の廃止日および除・売却日、および指定されている場合は置換。影響を受けるデプロイメントを特定し、移行候補を評価します。
フィルタモデルID、レルム、リージョン、機能、API機能、サービング・モード、アクセス・タイプおよびライフサイクル・ステータス。フォーカスされたモデル・ピッカーを作成するか、カタログ検索を自動化します。

個々のモデルがすべてのフィールドに移入されるとはかぎりません。検出は、モデルの選択と構成に役立ちます。推論、アクセス権の付与またはベンチマーク・モデルの品質は起動しません。選択したモデルと、アプリケーションのワークロードとの置換候補を検証します。

このチュートリアルでは、OCI IAMセッションによる認証、Python SDKによる問合せ検出、REST経由での同じリクエストの送信、および10の実際の例を試みます。コンパートメントにすでにインポートされているモデルをリストする方法についても学習します。

ノート

この操作はListModelDiscoveryです。これは、list_models()、list_imported_models()およびOpenAI互換の/modelsエンドポイントとは異なります。検出リクエストをシカゴに送信しても、カタログ結果はシカゴに制限されません。regionフィルタを使用して、カタログ・リージョンを選択します。

始める前に

このチュートリアルを完了するには、次のものが必要です。

  • モデルを検査できるOCIアカウントおよびコンパートメント。ListModelDiscoveryにはGENERATIVE_AI_MODEL_INSPECTが必要です。モデル権限を参照してください。
  • import-modelの例では、選択したコンパートメント内のインポートされたモデルおよび既存のインポートされたリソースを検出する権限があります。インポートされたモデル権限を参照してください。
  • Python 3.11以降、uvおよびインストール済のOCI CLI。このコマンドは、macOS、LinuxまたはWindows Subsystem for LinuxでBash互換シェルを使用します。
  • OCI CLIセッション認証およびサービス・エンドポイントへのアクセス用のブラウザ。これらの例では、OC1商用レルムでUS Midwest (Chicago)を使用しています。

この例では、セッション・トークンとその秘密キーを使用したOCI IAMリクエスト署名を使用します。OCI生成AI APIキーは個別の資格証明であり、ここで使用される署名者を置き換えるものではありません。

1. 環境の準備およびサインイン

  1. Python仮想環境を作成してアクティブ化し、パッケージをインストールします:
    uv venv --python 3.11 .venv
    source .venv/bin/activate
    uv pip install --upgrade oci requests

    環境がすでに存在する場合は、パッケージをインストールまたはアップグレードする前にそれをアクティブにします。

  2. インストールされているSDKによって検出方法が提供され、OCI CLIが使用可能であることを確認します:
    python -c 'import oci; print(oci.__version__); print(hasattr(oci.generative_ai.GenerativeAiClient, "list_model_discovery"))'
    oci --version

    メソッド・チェックでは、Trueが出力されます。Falseを出力する場合は、list_model_discoveryを含むSDKバージョンをインストールします。

  3. このシェルのサンプル・プロファイル、エンドポイント・リージョンおよびコンパートメントを設定します:
    export OCI_DISCOVERY_PROFILE=GENAI_DISCOVERY
    export OCI_DISCOVERY_REGION=us-chicago-1
    export OCI_COMPARTMENT_ID='<your-compartment-ocid>'

    コンパートメント・プレースホルダを、アクセス可能なコンパートメントに置き換えます。GENAI_DISCOVERYは、プロファイル名の例です。スクリプトは、デフォルトのコンパートメントを使用せずにこれらの環境変数を読み取ります。

  4. CLIセッションを作成し、ブラウザのサインインを完了します。
    oci session authenticate \
      --profile-name "$OCI_DISCOVERY_PROFILE" \
      --region "$OCI_DISCOVERY_REGION"

    CLIは、プロファイルを~/.oci/configに格納します。Pythonの例では、プロファイルのセッション・トークンおよび秘密キーをSecurityTokenSignerでロードします。セッションが期限切れになったら、再度認証し、スクリプトまたは対話型のPythonセッションを再起動します。「CLI用のトークンベース認証」を参照してください。

2.Python SDKを使用した問合せ検出

この例では、選択したリージョンの現在のオンデマンド・チャット・モデルを検索します。ページ区切りに従い、モデルID、ベンダーおよび機能が出力されます。ヘルパー関数は、後の例で再利用します。

  1. 次のコードをmodel_discovery.pyとして作業ディレクトリに保存します。
    """Query OCI Generative AI model metadata with an IAM session."""
    
    import os
    import re
    import unicodedata
    from pathlib import Path
    
    import oci
    
    
    def load_session():
        config = oci.config.from_file(
            profile_name=os.environ["OCI_DISCOVERY_PROFILE"]
        )
        config["region"] = os.environ["OCI_DISCOVERY_REGION"]
        token = Path(config["security_token_file"]).expanduser().read_text(
            encoding="utf-8"
        ).strip()
        private_key = oci.signer.load_private_key_from_file(
            str(Path(config["key_file"]).expanduser()),
            config.get("pass_phrase"),
        )
        signer = oci.auth.signers.SecurityTokenSigner(token, private_key)
        return config, signer
    
    
    def make_client():
        config, signer = load_session()
        return oci.generative_ai.GenerativeAiClient(config, signer=signer)
    
    
    def fetch_models(client, compartment_id, source="discovery", **filters):
        if source not in {"discovery", "imported"}:
            raise ValueError("source must be discovery or imported")
        operation = (
            client.list_model_discovery
            if source == "discovery"
            else client.list_imported_models
        )
        items = []
        while True:
            response = operation(compartment_id=compartment_id, **filters)
            items.extend(oci.util.to_dict(response.data)["items"])
            next_page = response.headers.get("opc-next-page")
            if not next_page:
                return items
            filters["page"] = next_page
    
    
    def normalized(value):
        value = unicodedata.normalize("NFKC", str(value)).casefold()
        return " ".join(re.findall(r"[^\W\d_]+|\d+", value))
    
    
    def search_models(models, query):
        terms = [normalized(term) for term in query.split()]
        terms = [term for term in terms if term]
    
        def matches(model):
            values = [
                model.get(field)
                for field in ("model_id", "id", "display_name", "vendor", "version")
            ]
            values.extend(
                value for value in (model.get("data_source") or {}).values()
                if isinstance(value, str)
            )
            candidates = [normalized(value) for value in values if value]
            return all(
                any(
                    re.search(
                        (r"(?<!\d)" if term[0].isdigit() else "")
                        + re.escape(term),
                        value,
                    )
                    for value in candidates
                )
                for term in terms
            )
    
        return [model for model in models if matches(model)]
    
    
    def show(models):
        print(f"{len(models)} matching model(s)")
        for model in models:
            name = (
                model.get("model_id")
                or model.get("display_name")
                or model.get("id")
            )
            capabilities = ", ".join(model.get("capabilities") or [])
            vendor = model.get("vendor") or "not reported"
            print(f"{name} | vendor={vendor} | capabilities={capabilities}")
    
    
    if __name__ == "__main__":
        client = make_client()
        models = fetch_models(
            client,
            os.environ["OCI_COMPARTMENT_ID"],
            region=[os.environ["OCI_DISCOVERY_REGION"]],
            capability=["CHAT"],
            serving_mode=["ON_DEMAND"],
            is_deprecated=False,
            is_on_demand_retired=False,
        )
        show(models)
  2. アクティブ化された環境でスクリプトを実行します。
    python model_discovery.py
  3. 一致するモデルをレビューし、検査する識別子をコピーします。

    結果は、現在のカタログおよびフィルタによって異なります。空の結果は、モデルをインポートできない認証テストまたは証明ではありません。

    SDKは、config["region"]から管理エンドポイントを導出します。個別のregion=[...]引数は、カタログの可用性をフィルタ処理します。どちらの値も推論エンドポイントを構成しません。

    メソッドの詳細は、GenerativeAiClient.list_model_discoveryを参照してください。

ローカル検索およびインポートされたモデル・インベントリ用のページ区切り検出問合せおよび再使用可能な関数があります。

3. 検出REST APIの直接コール

直接リクエストは、HTTPメソッド、パス、問合せパラメータおよびレスポンス・ヘッダーを公開します。これは、SDKと同じ検出操作を呼び出します。リクエストには、まだOCI IAMの認可が必要です。

GET /20231130/modelDiscovery?compartmentId=<your-compartment-ocid>
  1. シカゴのパブリック管理エンドポイントを設定します。
    export OCI_DISCOVERY_ENDPOINT=https://generativeai.us-chicago-1.oci.oraclecloud.com

    この例では、OC1 Chicagoエンドポイントを使用します。別のリージョンまたはレルムの場合は、APIリファレンスで適切な管理エンドポイントを使用し、カタログ・フィルタのOCI_DISCOVERY_REGIONを更新します。Discoveryでは、チャット推論URLではなく管理APIが使用されます。

  2. OCI CLIで署名付きリクエストを送信します:
    oci raw-request \
      --profile "$OCI_DISCOVERY_PROFILE" \
      --auth security_token \
      --http-method GET \
      --target-uri "${OCI_DISCOVERY_ENDPOINT}/20231130/modelDiscovery?compartmentId=${OCI_COMPARTMENT_ID}&region=${OCI_DISCOVERY_REGION}&capability=CHAT&servingMode=ON_DEMAND&isDeprecated=false&isOnDemandRetired=false"

    CLIはリクエストに署名し、レスポンスを表示します。別のページを取得するには、戻されたopc-next-page値をURLエンコードされたpage問合せパラメータとして渡します。次のPythonの例は、自動的にページ区切りに従います。

  3. 次のコードをmodel_discovery_rest.pyとしてmodel_discovery.pyとともに保存します。

    このスクリプトは、SDKの例からセッション署名ヘルパーをインポートします。requestsを介してHTTPリクエストを送信し、ステータスおよびリクエストIDを標準エラーに出力します。

    """Call Model Discovery over HTTP with OCI request signing."""
    
    import os
    import sys
    
    import requests
    
    from model_discovery import load_session
    
    
    def fetch_rest_models(signer, endpoint, compartment_id, region):
        url = endpoint.rstrip("/") + "/20231130/modelDiscovery"
        params = {
            "compartmentId": compartment_id,
            "region": [region],
            "capability": ["CHAT"],
            "servingMode": ["ON_DEMAND"],
            "isDeprecated": "false",
            "isOnDemandRetired": "false",
        }
        items = []
        while True:
            response = requests.get(
                url,
                params=params,
                headers={"Accept": "application/json"},
                auth=signer,
                timeout=(10, 30),
                allow_redirects=False,
            )
            request_id = response.headers.get("opc-request-id", "not reported")
            print(
                f"HTTP {response.status_code} | request ID: {request_id}",
                file=sys.stderr,
            )
            if 300 <= response.status_code < 400:
                raise RuntimeError("Check the service endpoint. Redirect not followed.")
            response.raise_for_status()
            items.extend(response.json()["items"])
            next_page = response.headers.get("opc-next-page")
            if not next_page:
                return items
            params["page"] = next_page
    
    
    if __name__ == "__main__":
        _, signer = load_session()
        models = fetch_rest_models(
            signer,
            os.environ["OCI_DISCOVERY_ENDPOINT"],
            os.environ["OCI_COMPARTMENT_ID"],
            os.environ["OCI_DISCOVERY_REGION"],
        )
        print(f"{len(models)} matching model(s)")
        for model in models:
            print(f"{model['modelId']} | vendor={model.get('vendor') or 'not reported'}")
  4. RESTの例を実行します。
    python model_discovery_rest.py
  5. モデル識別子とSDKの結果を比較します。

    比較には同じプロファイル、コンパートメント、エンドポイント・リージョンおよびフィルタを使用します。結果は、カタログの変更に応じて変更できます。

    RESTレスポンス・フィールドは、modelIdなどのcamelCaseを使用します。SDKのoci.util.to_dict()出力では、model_idなどのsnake_caseが使用されます。リスト値RESTフィルタの場合は、問合せキー(region=us-chicago-1&region=us-ashburn-1など)を繰り返します。requestsパッケージは、この方法でparamsのリストをエンコードします。

4. 実用的な例を準備する

  1. この設定をmodel_discovery_recipes.pyとしてmodel_discovery.pyとともに保存します。
    import json
    import os
    from collections import Counter
    from pathlib import Path
    
    from model_discovery import fetch_models, make_client, search_models, show
    
    client = make_client()
    compartment_id = os.environ["OCI_COMPARTMENT_ID"]
    region = os.environ["OCI_DISCOVERY_REGION"]
    
    
    def fetch(source="discovery", **filters):
        return fetch_models(client, compartment_id, source=source, **filters)
  2. セットアップがロードされた状態でPythonを起動します。
    python -i model_discovery_recipes.py

    -iオプションは Pythonを開いたままにします。そのセッションの次のセクションで、任意の例を実行します。または、設定ファイルに例を追加し、-iなしで実行します。

返されたカタログからのフィルタの選択

フィルタを選択する前に、リージョン内のモデルについてレポートされた値を調べます:

catalog = fetch(region=[region])
for field in ("capabilities", "api_capability", "model_access", "vendor"):
    counts = Counter()
    for model in catalog:
        values = model.get(field) or []
        if isinstance(values, str):
            values = [values]
        counts.update(set(values))
    print(field, dict(sorted(counts.items())))

for field in ("realm", "region", "serving_modes"):
    counts = Counter()
    for model in catalog:
        values = set()
        for availability in model.get("availability") or []:
            value = availability.get(field)
            if isinstance(value, list):
                values.update(value)
            elif value:
                values.add(value)
        counts.update(values)
    print(field, dict(sorted(counts.items())))

各カウントは、値に関連付けられた返されたモデル・エントリの数です。モデルは、複数の機能または可用性の値に寄与する可能性があります。カウントには、クエリー・フィルタが反映されます。

SDK引数例または目的
region, realmリージョン識別子またはレルム値のリスト。カタログによってレポートされた値を使用します。
model_id正確なモデル識別子。
capability["CHAT"]、["TEXT_EMBEDDINGS"]、["TEXT_RERANK"]などのリスト。
api_capabilityカタログによってレポートされるAPI識別子のリスト。
serving_mode["ON_DEMAND"]または["DEDICATED"]。
model_access["HOSTED"]または["PROXY"]。
is_deprecatedFalseは、非推奨のモデルを除外します。
is_on_demand_retired, is_dedicated_retiredリタイアしたモデルを除外するサービング・モードには、Falseを使用します。
page, limitページ区切りトークンおよびリクエストされたページ・サイズ。この例は、すべてのページが取得されるまでopc-next-pageの後に続きます。

すべてのページをフェッチした後のローカル・サブストリング一致には、search_models()を使用します。検索はListModelDiscoveryのパラメータではありません。完全なフィルタ語彙については、操作リファレンスを確認してください。

10の実用的なPythonの例

1. 両方のサービング・モード間でのモデルの参照

目的のデプロイメント・リージョンでフィルタします。serving_modeを省略すると、オンデマンド・モデルと専用モデルの両方が適格になります。

catalog = fetch(region=[region])
show(catalog)

2. 部分名またはベンダーによるモデルの検索

返されたメタデータをローカルで検索します。スペースで区切られたすべての用語は、検索可能なフィールドに一致する必要があります。マッチングでは、大文字と小文字の区別は無視されます。サービスのmodel_idフィルタには、正確な識別子が必要です。

catalog = fetch(region=[region])
show(search_models(catalog, "grok"))

結果の検索語をプロバイダまたはモデル・ファミリに変更します。

3. 現在のオンデマンド・チャット・モデルの検索

オンデマンド・サービスのために廃止された非推奨のモデルおよびモデルを除外します。

show(fetch(
    region=[region],
    capability=["CHAT"],
    serving_mode=["ON_DEMAND"],
    is_deprecated=False,
    is_on_demand_retired=False,
))

4. 専用サービスのモデルの検索

退職フィルタを専用サービングに適用します。この問合せは、検出カタログを検索します。インポートしたリソースをリストするには、例8を使用します。

show(fetch(
    region=[region],
    serving_mode=["DEDICATED"],
    is_deprecated=False,
    is_dedicated_retired=False,
))

5. 埋込みおよび再ランク付けモデルの検索

各機能を個別に問い合せます。検出では、TEXT_EMBEDDINGSおよびTEXT_RERANKが使用されます。インポートされたモデル・リソースでは、EMBEDDINGやRERANKなど、異なる機能語彙が使用されます。

for capability in ("TEXT_EMBEDDINGS", "TEXT_RERANK"):
    print("\nCapability:", capability)
    show(fetch(region=[region], capability=[capability]))

6. レポートされたAPI機能によるプロキシ・モデルの検索

カタログから返されたAPI識別子を読み取り、値をコピーしてプロキシ・モデルをフィルタします。選択したモデルの外部コール・ノートおよびデータ処理を確認します。

catalog = fetch(region=[region], model_access=["PROXY"])
api_values = sorted({
    api for model in catalog for api in model.get("api_capability") or []
})
print(api_values)
if api_values:
    api = input("Enter an API capability from the list: ").strip()
    if api in api_values:
        show(fetch(
            region=[region],
            model_access=["PROXY"],
            api_capability=[api],
        ))
    else:
        print("Enter an exact value from the returned list.")
else:
    print("No API capabilities were reported for these results.")

7. モデル・パラメータおよびライフサイクルの詳細の検査

結果から正確なモデル識別子をコピーします。完全な可用性エントリを検査して、各リージョンをサービス・モード、ライフサイクル日付およびレポートされた置換に関連付けます。

model_id = input("Enter an exact model ID from your results: ").strip()
for model in fetch(model_id=model_id):
    print(model["model_id"])
    for field in (
        "api_capability", "modality_support", "parameters", "availability"
    ):
        print(field)
        print(json.dumps(model.get(field), indent=2, default=str))

フィールドには、空または未報告を指定できます。移行する前に、アプリケーションの置換候補を評価します。

8. インポートされたQwenモデルの検索

source="imported"を使用して、コンパートメントおよびクライアント・リージョンの既存のインポートを問い合せます。この例では、Qwen 3.6 35Bモデルを検索します。照合モデルをインポートし、そのリソースを検査できる場合にのみ、照合が返されます。インポートの互換性については、Compatible Qwen 3.6 Modelsを参照してください。

imports = fetch(source="imported")
selected = search_models(imports, "qwen 3.6 35B")
show(selected)
for model in selected:
    print("Imported resource OCID:", model["id"])
    print("Resource state:", model.get("lifecycle_state"))

返されるOCIDは、インポートされたモデル・リソースを識別します。ACTIVEリソース状態では、推論エンドポイントがデプロイされたことが証明されません。デプロイメント・ステップは、「インポートされたモデルの管理」を参照してください。

9。検出とインポート済リソースの比較

モデルがインポートされたインベントリに表示されるが、検出されない場合は、2つのソースを比較します。検出結果およびコンパートメントにインポートされたリソースは、様々な質問に回答します。

print("Discovery catalog for the selected region:")
show(search_models(fetch(region=[region]), "qwen"))
print("\nImported resources in this compartment and client region:")
show(search_models(fetch(source="imported"), "qwen"))

インポートされた結果が空の場合は、コンパートメント、クライアント・リージョンおよび権限を確認してください。インポートされたモデル問合せでは、他のコンパートメントまたはリージョンはスキャンされません。

10。一致するインポート済モデルのエクスポート

選択したインポート済リソースの完全なメタデータをローカルJSONファイルに書き込みます。

selected = search_models(fetch(source="imported"), "qwen 3.6 35B")
Path("qwen-imports.json").write_text(
    json.dumps({"items": selected}, indent=2, default=str),
    encoding="utf-8",
)
print(f"Saved {len(selected)} matching resource(s) to qwen-imports.json")

結果の確認とトラブルシューティング

リクエストが成功すると、モデル・メタデータまたはエントリが一致しない場合は空のリストが返されます。推論リクエストを作成する前に、識別子、機能、パラメータおよび可用性をレビューします。

現象確認対象
SDKにはlist_model_discoveryメソッドがありません。アクティブな仮想環境でociをアップグレードし、メソッド・チェックを繰り返します。
署名されたリクエストはHTTP 401を返します。CLIセッションを検証または再作成し、選択したプロファイルを確認し、Pythonを再起動して新しいトークンを読み取ります。
サービスは承認エラーを報告します。IAM権限およびコンパートメントOCIDを確認します。インポートされた在庫には、インポートされたモデル・リソースを検査する権限が必要です。
正確なモデルIDフィルタは結果を返しません。最初にローカルを参照または検索し、レスポンスから識別子をコピーします。リージョンおよびライフサイクル・フィルタを確認します。
レスポンスには、エンドポイント・リージョン外の可用性が含まれます。検出regionフィルタを明示的に設定し、各可用性エントリを検査します。エンドポイント・リージョンのみでは、カタログ結果は制約されません。
インポートされたモデルは返されません。クライアントのリージョン、コンパートメント、権限およびリソース表示名を確認します。インポートされたインベントリにlist_imported_models()を使用します。
カタログの一部のみが返されます。opc-next-pageに従います。このページのPythonの例は、すべてのページを取得します。
SDKが成功している間、RESTリクエストは失敗します。同じプロファイルおよびコンパートメントを使用します。管理エンドポイント、パスおよびcamelCase問合せ名を確認し、ステータスおよびリクエストIDを確認します。

生成されたCLIコマンドについては、list-model-discoveryを参照してください。インポートされたリソースについては、ListImportedModelsを参照してください。検出によって返されるメタデータ・フィールドについては、ModelDiscoveryを参照してください。