"use strict";
/**
 * Standalone Gemma adapter (F-NEW-GEMMA-ADAPTER, Tier 4.5).
 *
 * Gemma is Google's open-weight family. Unlike Gemini (proprietary, Google-
 * hosted only), Gemma can be served from multiple deployment targets:
 *
 *   - `vertex-ai`           — Google Cloud Vertex AI Model Garden. Inherits
 *                             Vertex BAA. Same auth as Gemini-on-Vertex.
 *   - `generative-language` — Google AI Studio public endpoint. NO BAA.
 *   - `huggingface`         — HuggingFace Inference API. Open-weight pull.
 *                             BAA only if customer holds one with HF.
 *   - `self-hosted-ollama`  — Customer-owned host (Ollama, vLLM, TGI, etc.).
 *                             BAA is moot (customer owns the runtime).
 *
 * This adapter is a thin facade. It centralises the open-weight provenance
 * fact + the deployment-target-aware BAA flag + the model-ID registry so
 * the capability bundle + audit chain entry + dashboard surface all read
 * Gemma usage from one source of truth instead of inferring it from
 * `getGoogleModelFamily()` in twelve places.
 *
 * Wiring downstream:
 *   - `vertex-ai` + `generative-language` requests delegate to
 *     `src/llm-adapters/google.ts` (the Google adapter knows both targets).
 *   - `huggingface` delegates to `src/llm-adapters/huggingface.ts`.
 *   - `self-hosted-ollama` delegates to `src/llm-adapters/ollama.ts`.
 *   The downstream wiring is the customer's existing adapter call —
 *   `gemma.ts` itself does NOT make HTTP. It is metadata + validation.
 *
 * @connexum/ai-governance
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.KNOWN_GEMMA_MODEL_IDS = void 0;
exports.validateGemmaModelId = validateGemmaModelId;
exports.getGemmaMetadata = getGemmaMetadata;
exports.isGemmaMultimodal = isGemmaMultimodal;
exports.gemmaSubfamilyOf = gemmaSubfamilyOf;
exports.validateGemmaFinetuneProvenance = validateGemmaFinetuneProvenance;
exports.getGemmaFinetuneMetadata = getGemmaFinetuneMetadata;
exports.isGemmaFinetuneIdShape = isGemmaFinetuneIdShape;
const google_1 = require("./google");
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
/**
 * Frozen re-export of the Gemma model IDs the platform recognises out of
 * the box. The list lives in `google.ts` because that's where the actual
 * Vertex / Generative-Language wire-handling sits. Re-exposed here so
 * customers / dashboards / catalog endpoints only need to depend on
 * `@connexum/ai-governance/adapters/gemma`, not the Google adapter.
 */
exports.KNOWN_GEMMA_MODEL_IDS = Object.freeze([...google_1.KNOWN_GEMMA_MODEL_IDS]);
const ALLOWED_DEPLOYMENT_TARGETS = Object.freeze([
    'vertex-ai',
    'generative-language',
    'huggingface',
    'self-hosted-ollama',
]);
// ---------------------------------------------------------------------------
// Validation
// ---------------------------------------------------------------------------
/**
 * Returns true if `modelId` is a recognised Gemma model ID. Recognition is
 * either (a) exact match against `KNOWN_GEMMA_MODEL_IDS`, or (b) lowercase
 * `gemma-` / `paligemma-` prefix. Future Gemma 3 / 4 IDs validate via the
 * prefix path without requiring this file to change every release.
 *
 * Case-sensitive — "Gemma-2-9b-it" with a capital G is rejected. Defends
 * against typo / copy-paste from marketing material that uses Title Case.
 */
function validateGemmaModelId(modelId) {
    if (typeof modelId !== 'string' || modelId.length === 0)
        return false;
    if (exports.KNOWN_GEMMA_MODEL_IDS.includes(modelId))
        return true;
    // Reuse Google adapter's lexical family detection — same prefix rules.
    // Case-sensitive: getGoogleModelFamily lower-cases internally but we
    // want strict matching here, so we check the prefix directly.
    if (modelId.startsWith('gemma-') || modelId.startsWith('paligemma-')) {
        return true;
    }
    return false;
}
// ---------------------------------------------------------------------------
// Metadata
// ---------------------------------------------------------------------------
/**
 * Produce the metadata bundle for a Gemma call. Throws on unknown
 * deployment target so a typo at the call site fails loudly instead of
 * silently fabricating a BAA flag.
 */
function getGemmaMetadata(modelId, deploymentTarget) {
    if (!ALLOWED_DEPLOYMENT_TARGETS.includes(deploymentTarget)) {
        throw new Error(`Unknown Gemma deployment target: ${JSON.stringify(deploymentTarget)}. ` +
            `Expected one of: ${ALLOWED_DEPLOYMENT_TARGETS.join(', ')}.`);
    }
    // BAA inheritance: only Vertex AI carries an inherited BAA today. The
    // other three either have no BAA (generative-language) or place the
    // burden on the customer (huggingface, self-hosted-ollama).
    const baaInherits = deploymentTarget === 'vertex-ai';
    // displayName must carry BOTH modelId AND deployment target so audit
    // consumers can distinguish the same model served by different hosters.
    const targetLabel = {
        'vertex-ai': 'Vertex AI',
        'generative-language': 'Google AI Studio',
        'huggingface': 'HuggingFace Inference',
        'self-hosted-ollama': 'Self-hosted (Ollama)',
    };
    const displayName = `Google Gemma (open-weight): ${modelId} via ${targetLabel[deploymentTarget]}`;
    return {
        family: 'gemma',
        isOpenWeight: true,
        baaInherits,
        displayName,
        deploymentTarget,
    };
}
// ---------------------------------------------------------------------------
// Multimodal detection (passthrough)
// ---------------------------------------------------------------------------
/**
 * Returns true if the model is multimodal (image+text in). Passthrough to
 * `google.ts isGemmaMultimodal()` to keep one source of truth. Returns
 * false for non-Gemma IDs.
 */
function isGemmaMultimodal(modelId) {
    if (!validateGemmaModelId(modelId))
        return false;
    // For non-recognised-prefix IDs the Google helper will still answer
    // false, so we don't need to short-circuit further.
    return (0, google_1.isGemmaMultimodal)(modelId);
}
// ---------------------------------------------------------------------------
// Family sanity check (compile-time helper for downstream wiring)
// ---------------------------------------------------------------------------
/**
 * Returns 'gemma' if the modelId belongs to the Gemma family per the
 * shared Google adapter classifier, or null otherwise. Useful when a
 * caller has an arbitrary modelId and wants to route to the standalone
 * Gemma adapter only on a definite match.
 */
function gemmaSubfamilyOf(modelId) {
    return (0, google_1.getGoogleModelFamily)(modelId) === 'gemma' ? 'gemma' : null;
}
// ---------------------------------------------------------------------------
// F-NEW-GEMMA-FINETUNE 2026-05-13
// Customer-finetuned Gemma support.
// ---------------------------------------------------------------------------
const FINETUNE_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{2,127}$/;
/**
 * Validate a `GemmaFinetuneProvenance` payload. Returns `null` on success
 * or a string error message describing the first problem. Validates:
 *
 *   - `baseModelId` is a recognised Gemma model ID per validateGemmaModelId
 *   - `finetuneId` matches FINETUNE_ID_PATTERN (3-128 alphanumeric/._-)
 *   - `finetuneHost` is an allowed deployment target
 *   - `datasetHash` (when supplied) is a hex SHA-256 (64 chars)
 *   - `finetunedAt` (when supplied) parses to a Date
 *   - `baaOverride` is boolean when present
 *
 * Customers SHOULD supply `datasetHash` + `finetunedAt`; absence is
 * surfaced by the pack validators downstream (HIGH severity), not here.
 */
function validateGemmaFinetuneProvenance(p) {
    if (!p || typeof p !== 'object')
        return 'finetune provenance must be an object';
    if (!validateGemmaModelId(p.baseModelId)) {
        return `baseModelId is not a recognised Gemma model: ${JSON.stringify(p.baseModelId)}`;
    }
    if (typeof p.finetuneId !== 'string' || !FINETUNE_ID_PATTERN.test(p.finetuneId)) {
        return `finetuneId must match ${FINETUNE_ID_PATTERN.source} (got ${JSON.stringify(p.finetuneId)})`;
    }
    if (!ALLOWED_DEPLOYMENT_TARGETS.includes(p.finetuneHost)) {
        return `finetuneHost must be one of: ${ALLOWED_DEPLOYMENT_TARGETS.join(', ')} (got ${JSON.stringify(p.finetuneHost)})`;
    }
    if (p.datasetHash !== undefined) {
        if (typeof p.datasetHash !== 'string' || !/^[0-9a-fA-F]{64}$/.test(p.datasetHash)) {
            return `datasetHash must be a 64-char hex SHA-256 (got length ${p.datasetHash?.length ?? 0})`;
        }
    }
    if (p.finetunedAt !== undefined) {
        if (typeof p.finetunedAt !== 'string' || Number.isNaN(Date.parse(p.finetunedAt))) {
            return `finetunedAt must be a parseable ISO-8601 timestamp (got ${JSON.stringify(p.finetunedAt)})`;
        }
    }
    if (p.baaOverride !== undefined && typeof p.baaOverride !== 'boolean') {
        return `baaOverride must be boolean when present`;
    }
    return null;
}
/**
 * Construct the metadata bundle for a finetuned Gemma call. The
 * `effectiveModelId` is what the customer's runtime ACTUALLY invokes
 * (could be a custom slug like `acme-health/gemma-2-9b-it-ft`); the
 * `provenance.baseModelId` is the underlying recognised Gemma the
 * finetune derived from.
 *
 * BAA semantics: the base-model deployment target sets `baaInherits`;
 * the finetune host independently sets `provenance.baaOverride`. The
 * returned metadata's `baaInherits` is the OR of both — auditors care
 * about coverage, not which leg of the deployment provided it.
 *
 * Throws on invalid provenance via validateGemmaFinetuneProvenance —
 * adapter callers MUST handle this (do not silently fabricate finetune
 * metadata, which would let an unprovenanced finetune slip into the
 * audit chain tagged as "compliant").
 */
function getGemmaFinetuneMetadata(effectiveModelId, baseDeploymentTarget, provenance) {
    const err = validateGemmaFinetuneProvenance(provenance);
    if (err) {
        throw new Error(`Invalid Gemma finetune provenance: ${err}`);
    }
    // Build the base metadata first, then graft the finetune block on top.
    // The base call validates baseDeploymentTarget for us.
    const base = getGemmaMetadata(provenance.baseModelId, baseDeploymentTarget);
    const baaInherits = base.baaInherits || provenance.baaOverride === true;
    const finetunedSuffix = provenance.datasetHash
        ? ` (finetune ${provenance.finetuneId}, dataset ${provenance.datasetHash.slice(0, 8)}…)`
        : ` (finetune ${provenance.finetuneId})`;
    return {
        ...base,
        baaInherits,
        displayName: `${base.displayName}${finetunedSuffix}`,
        finetune: provenance,
    };
}
/**
 * Heuristic detector — returns true when `modelId` looks like a Gemma
 * finetune (i.e. carries a customer suffix or org namespace beyond the
 * base model ID). Used by callers that want to route a request to the
 * finetune-aware path without needing the customer to flag it explicitly.
 *
 * Rules:
 *   - HuggingFace `<org>/<base>-<anything>` where <base> validates
 *   - Vertex `<base>@<custom>` per Vertex finetune-suffix convention
 *   - Self-hosted `<base>-<3+chars>` where the trailing slug is NOT in
 *     the recognised KNOWN_GEMMA_MODEL_IDS list (excludes things like
 *     `gemma-2-9b` → `gemma-2-9b-it` which are base variants, not
 *     finetunes).
 *
 * Returns false on non-Gemma inputs, on bare base-model IDs, and on
 * anything outside the three recognised conventions.
 */
function isGemmaFinetuneIdShape(modelId) {
    if (typeof modelId !== 'string' || modelId.length === 0)
        return false;
    // HuggingFace org-namespaced shape: <org>/<rest> where <rest> looks
    // like a Gemma model (prefix match) but is NOT the bare known base.
    // E.g. acme/gemma-2-9b-it-ft is a finetune; google/gemma-2-9b-it is
    // just the base model on HF (still false here — bare base).
    if (modelId.includes('/')) {
        const tail = modelId.split('/').slice(-1)[0];
        if (exports.KNOWN_GEMMA_MODEL_IDS.includes(tail))
            return false;
        return /^(gemma|paligemma)-/i.test(tail);
    }
    // Vertex custom-suffix shape: <base>@<custom>
    if (modelId.includes('@')) {
        const base = modelId.split('@')[0];
        return validateGemmaModelId(base);
    }
    // Self-hosted slug shape: <base>-<suffix not in KNOWN list>
    if (exports.KNOWN_GEMMA_MODEL_IDS.includes(modelId))
        return false;
    if (!(modelId.startsWith('gemma-') || modelId.startsWith('paligemma-')))
        return false;
    // Otherwise: it has the prefix but isn't in the known list — assume finetune.
    return true;
}
//# sourceMappingURL=gemma.js.map