feat(ql3): establish 3.0 incubation baseline

This commit is contained in:
whyour
2026-08-12 00:25:26 +08:00
parent 4bf92dcfeb
commit c699c32461
2817 changed files with 779642 additions and 653 deletions
@@ -0,0 +1,68 @@
export const LOCAL_MODEL_INVOCATION_MIGRATION_STREAM_ID =
'ql3-ai-model-invocation-sqlite';
export const LOCAL_MODEL_INVOCATION_MIGRATION_ID = '9001-ai-model-invocations';
export const LOCAL_MODEL_INVOCATION_USAGE_MIGRATION_ID =
'9002-ai-model-usage-ledger';
export const LOCAL_MODEL_INVOCATION_QUOTA_MIGRATION_ID =
'9003-ai-model-usage-quota';
export const LOCAL_MODEL_INVOCATION_PRICING_MIGRATION_ID =
'9004-ai-model-pricing-snapshots';
export const LOCAL_MODEL_PRICE_CATALOG_MIGRATION_ID =
'9005-ai-model-price-catalog';
export const LOCAL_MODEL_PRICE_CATALOG_AUTHORIZATION_MIGRATION_ID =
'9006-ai-model-price-catalog-authorizations';
export const LOCAL_MODEL_INVOCATION_FEATURE_ACTIVATION_MIGRATION_ID =
'9007-ai-feature-activation';
export const LOCAL_PLUGIN_PACKAGE_PROMPT_ADMISSION_MIGRATION_ID =
'9008-ai-plugin-package-prompt-admissions';
export const LOCAL_PLUGIN_PACKAGE_PROMPT_FINALIZATION_MIGRATION_ID =
'9009-ai-plugin-package-prompt-finalizations';
export const LOCAL_PLUGIN_PACKAGE_PROMPT_OUTPUT_ARTIFACT_MIGRATION_ID =
'9010-ai-plugin-package-prompt-output-artifacts';
export const LOCAL_PLUGIN_PACKAGE_PROMPT_OUTPUT_TOMBSTONE_MIGRATION_ID =
'9011-ai-plugin-package-prompt-output-tombstones';
export const LOCAL_PLUGIN_PACKAGE_PROMPT_OUTPUT_KEY_RETIREMENT_MIGRATION_ID =
'9012-ai-plugin-package-prompt-output-key-retirements';
export const LOCAL_MODEL_PROVIDER_CREDENTIAL_CATALOG_MIGRATION_ID =
'9013-ai-model-provider-credential-catalog';
export const POSTGRES_MODEL_INVOCATION_MIGRATION_STREAM_ID =
'ql3-ai-model-invocation-postgresql';
export const POSTGRES_MODEL_INVOCATION_MIGRATION_ID =
'pg-9001-ai-model-invocations';
export const POSTGRES_MODEL_INVOCATION_USAGE_MIGRATION_ID =
'pg-9002-ai-model-usage-ledger';
export const POSTGRES_MODEL_INVOCATION_QUOTA_MIGRATION_ID =
'pg-9003-ai-model-usage-quota';
export const POSTGRES_MODEL_INVOCATION_PRICING_MIGRATION_ID =
'pg-9004-ai-model-pricing-snapshots';
export const POSTGRES_MODEL_PRICE_CATALOG_MIGRATION_ID =
'pg-9005-ai-model-price-catalog';
export const POSTGRES_MODEL_PRICE_CATALOG_AUTHORIZATION_MIGRATION_ID =
'pg-9006-ai-model-price-catalog-authorizations';
export const POSTGRES_PLUGIN_PACKAGE_PROMPT_ADMISSION_MIGRATION_ID =
'pg-9007-ai-plugin-package-prompt-admissions';
export const POSTGRES_PLUGIN_PACKAGE_PROMPT_FINALIZATION_MIGRATION_ID =
'pg-9008-ai-plugin-package-prompt-finalizations';
export const POSTGRES_PLUGIN_PACKAGE_PROMPT_OUTPUT_ARTIFACT_MIGRATION_ID =
'pg-9009-ai-plugin-package-prompt-output-artifacts';
export const POSTGRES_PLUGIN_PACKAGE_PROMPT_OUTPUT_TOMBSTONE_MIGRATION_ID =
'pg-9010-ai-plugin-package-prompt-output-tombstones';
export const POSTGRES_PLUGIN_PACKAGE_PROMPT_OUTPUT_KEY_RETIREMENT_MIGRATION_ID =
'pg-9011-ai-plugin-package-prompt-output-key-retirements';
export const POSTGRES_MODEL_PROVIDER_CREDENTIAL_CATALOG_MIGRATION_ID =
'pg-9012-ai-model-provider-credential-catalog';
export const POSTGRES_MODEL_PROVIDER_CREDENTIAL_MANAGEMENT_MIGRATION_ID =
'pg-9013-ai-model-provider-credential-management-boundary';
export const POSTGRES_MODEL_PROVIDER_CREDENTIAL_MANAGEMENT_IDENTITY_MIGRATION_ID =
'pg-9014-ai-model-provider-credential-management-identity-ledger';
export const POSTGRES_MODEL_PROVIDER_CREDENTIAL_TEST_CONNECTION_MIGRATION_ID =
'pg-9015-ai-model-provider-credential-test-connection';
export const POSTGRES_PLUGIN_PACKAGE_PROMPT_OUTPUT_KEY_ROTATION_MIGRATION_ID =
'pg-9016-ai-plugin-package-prompt-output-key-rotation';
export const POSTGRES_PLUGIN_PACKAGE_PROMPT_PRODUCT_AUTHORIZATION_MIGRATION_ID =
'pg-9017-ai-plugin-package-prompt-product-authorization';
export const POSTGRES_MODEL_INVOCATION_SCHEMA = 'ql3_ai';
export const LOCAL_MODEL_INVOCATION_MIGRATION_HISTORY_TABLE =
'QingLong3AiSchemaMigrations';
export const POSTGRES_MODEL_INVOCATION_MIGRATION_HISTORY_TABLE =
'ai_schema_migrations';
@@ -0,0 +1,259 @@
import type {
PostgresPool,
PostgresQueryable,
} from '@qinglong/runtime-core';
import {
MigrationStreamHistoryCorruptionError,
runMigrationStream,
type MigrationStreamDefinition,
type MigrationStreamRecord,
type MigrationStreamStore,
type MigrationStreamTransaction,
} from '@qinglong/runtime-core/migration-stream';
import {
POSTGRES_MODEL_INVOCATION_MIGRATION_STREAM_ID,
POSTGRES_MODEL_INVOCATION_MIGRATION_ID,
POSTGRES_MODEL_INVOCATION_USAGE_MIGRATION_ID,
POSTGRES_MODEL_INVOCATION_QUOTA_MIGRATION_ID,
POSTGRES_MODEL_INVOCATION_PRICING_MIGRATION_ID,
POSTGRES_MODEL_PRICE_CATALOG_MIGRATION_ID,
POSTGRES_MODEL_PRICE_CATALOG_AUTHORIZATION_MIGRATION_ID,
POSTGRES_PLUGIN_PACKAGE_PROMPT_ADMISSION_MIGRATION_ID,
POSTGRES_PLUGIN_PACKAGE_PROMPT_FINALIZATION_MIGRATION_ID,
POSTGRES_PLUGIN_PACKAGE_PROMPT_OUTPUT_ARTIFACT_MIGRATION_ID,
POSTGRES_PLUGIN_PACKAGE_PROMPT_OUTPUT_TOMBSTONE_MIGRATION_ID,
POSTGRES_PLUGIN_PACKAGE_PROMPT_OUTPUT_KEY_RETIREMENT_MIGRATION_ID,
POSTGRES_MODEL_PROVIDER_CREDENTIAL_CATALOG_MIGRATION_ID,
POSTGRES_MODEL_PROVIDER_CREDENTIAL_MANAGEMENT_MIGRATION_ID,
POSTGRES_MODEL_PROVIDER_CREDENTIAL_MANAGEMENT_IDENTITY_MIGRATION_ID,
POSTGRES_MODEL_PROVIDER_CREDENTIAL_TEST_CONNECTION_MIGRATION_ID,
POSTGRES_PLUGIN_PACKAGE_PROMPT_OUTPUT_KEY_ROTATION_MIGRATION_ID,
POSTGRES_PLUGIN_PACKAGE_PROMPT_PRODUCT_AUTHORIZATION_MIGRATION_ID,
POSTGRES_MODEL_INVOCATION_SCHEMA,
POSTGRES_MODEL_INVOCATION_MIGRATION_HISTORY_TABLE,
} from './identities';
import { postgresCatalogMigrations } from './postgres/catalog';
import { postgresCoreMigrations } from './postgres/core';
import { postgresCredentialMigrations } from './postgres/credential';
import {
postgresPromptBaseMigrations,
postgresPromptExtensionMigrations,
} from './postgres/prompt';
import { postgresUsagePricingMigrations } from './postgres/usagePricing';
import { historyRecord, type HistoryRow } from './shared';
const POSTGRES_HISTORY_IDENTITY = Object.freeze({
migrationIds: Object.freeze([
POSTGRES_MODEL_INVOCATION_MIGRATION_ID,
POSTGRES_MODEL_INVOCATION_USAGE_MIGRATION_ID,
POSTGRES_MODEL_INVOCATION_QUOTA_MIGRATION_ID,
POSTGRES_MODEL_INVOCATION_PRICING_MIGRATION_ID,
POSTGRES_MODEL_PRICE_CATALOG_MIGRATION_ID,
POSTGRES_MODEL_PRICE_CATALOG_AUTHORIZATION_MIGRATION_ID,
POSTGRES_PLUGIN_PACKAGE_PROMPT_ADMISSION_MIGRATION_ID,
POSTGRES_PLUGIN_PACKAGE_PROMPT_FINALIZATION_MIGRATION_ID,
POSTGRES_PLUGIN_PACKAGE_PROMPT_OUTPUT_ARTIFACT_MIGRATION_ID,
POSTGRES_PLUGIN_PACKAGE_PROMPT_OUTPUT_TOMBSTONE_MIGRATION_ID,
POSTGRES_PLUGIN_PACKAGE_PROMPT_OUTPUT_KEY_RETIREMENT_MIGRATION_ID,
POSTGRES_PLUGIN_PACKAGE_PROMPT_OUTPUT_KEY_ROTATION_MIGRATION_ID,
POSTGRES_MODEL_PROVIDER_CREDENTIAL_CATALOG_MIGRATION_ID,
POSTGRES_MODEL_PROVIDER_CREDENTIAL_MANAGEMENT_MIGRATION_ID,
POSTGRES_MODEL_PROVIDER_CREDENTIAL_MANAGEMENT_IDENTITY_MIGRATION_ID,
POSTGRES_MODEL_PROVIDER_CREDENTIAL_TEST_CONNECTION_MIGRATION_ID,
POSTGRES_PLUGIN_PACKAGE_PROMPT_PRODUCT_AUTHORIZATION_MIGRATION_ID,
]),
streamId: POSTGRES_MODEL_INVOCATION_MIGRATION_STREAM_ID,
dialect: 'postgresql' as const,
});
async function readPostgresHistory(
queryable: PostgresQueryable,
migrationId?: string,
): Promise<readonly MigrationStreamRecord[]> {
const result = await queryable.query<HistoryRow>(
`SELECT
migration_id AS "migrationId",
stream_id AS "streamId",
dialect,
checksum,
applied_at_ms AS "appliedAtMs"
FROM "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."${POSTGRES_MODEL_INVOCATION_MIGRATION_HISTORY_TABLE}"
WHERE stream_id = $1
${migrationId === undefined ? '' : 'AND migration_id = $2'}
ORDER BY migration_id`,
migrationId === undefined
? [POSTGRES_MODEL_INVOCATION_MIGRATION_STREAM_ID]
: [POSTGRES_MODEL_INVOCATION_MIGRATION_STREAM_ID, migrationId],
);
return result.rows.map((row) =>
historyRecord(row, POSTGRES_HISTORY_IDENTITY),
);
}
class PostgresModelInvocationMigrationStore
implements MigrationStreamStore<PostgresQueryable>
{
constructor(private readonly pool: PostgresPool) {}
async ensureHistory(): Promise<void> {
const result = await this.pool.query<Record<string, unknown>>(
`SELECT
to_regclass('ql3.schema_migrations') IS NOT NULL AS history,
to_regclass('ql3.runs') IS NOT NULL AS runs,
to_regclass('ql3.run_events') IS NOT NULL AS events,
to_regclass('ql3.step_runs') IS NOT NULL AS steps,
to_regclass('ql3.step_run_mutations') IS NOT NULL AS mutations`,
);
const row = result.rows[0];
if (
result.rows.length !== 1 ||
row?.history !== true ||
row.runs !== true ||
row.events !== true ||
row.steps !== true ||
row.mutations !== true
) {
throw new TypeError(
'PostgreSQL ModelInvocation feature requires the main migration stream',
);
}
await this.pool.query(
`CREATE SCHEMA IF NOT EXISTS "${POSTGRES_MODEL_INVOCATION_SCHEMA}"`,
);
await this.pool.query(
`REVOKE ALL ON SCHEMA "${POSTGRES_MODEL_INVOCATION_SCHEMA}" FROM PUBLIC`,
);
await this.pool.query(`
CREATE TABLE IF NOT EXISTS "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."${POSTGRES_MODEL_INVOCATION_MIGRATION_HISTORY_TABLE}" (
migration_id varchar(128) PRIMARY KEY,
stream_id varchar(64) NOT NULL
CONSTRAINT ql3_ai_schema_migrations_stream_check
CHECK (stream_id = '${POSTGRES_MODEL_INVOCATION_MIGRATION_STREAM_ID}'),
dialect varchar(16) NOT NULL
CONSTRAINT ql3_ai_schema_migrations_dialect_check
CHECK (dialect = 'postgresql'),
checksum char(64) NOT NULL
CONSTRAINT ql3_ai_schema_migrations_checksum_check
CHECK (checksum ~ '^[0-9a-f]{64}$'),
applied_at_ms bigint NOT NULL
CONSTRAINT ql3_ai_schema_migrations_applied_at_check
CHECK (applied_at_ms >= 0)
)
`);
await this.pool.query(
`REVOKE ALL ON TABLE
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."${POSTGRES_MODEL_INVOCATION_MIGRATION_HISTORY_TABLE}"
FROM PUBLIC`,
);
}
async listAll(): Promise<readonly MigrationStreamRecord[]> {
return readPostgresHistory(this.pool);
}
async findById(migrationId: string): Promise<MigrationStreamRecord | null> {
const rows = await readPostgresHistory(this.pool, migrationId);
if (rows.length > 1) {
throw new MigrationStreamHistoryCorruptionError(migrationId);
}
return rows[0] ?? null;
}
async transaction<T>(
work: (
transaction: MigrationStreamTransaction<PostgresQueryable>,
) => Promise<T>,
): Promise<T> {
const client = await this.pool.connect();
let began = false;
try {
await client.query('BEGIN');
began = true;
await client.query(`SELECT set_config('statement_timeout', $1, true)`, [
'15s',
]);
await client.query(`SELECT set_config('lock_timeout', $1, true)`, ['5s']);
await client.query(
`SELECT set_config('idle_in_transaction_session_timeout', $1, true)`,
['15s'],
);
await client.query(
'SELECT pg_advisory_xact_lock($1, $2)',
[0x514c, 0x0301],
);
const result = await work({
context: client,
findById: async (migrationId) => {
const rows = await readPostgresHistory(client, migrationId);
return rows[0] ?? null;
},
insert: async (record) => {
if (
record.streamId !== POSTGRES_MODEL_INVOCATION_MIGRATION_STREAM_ID ||
!POSTGRES_HISTORY_IDENTITY.migrationIds.includes(
record.migrationId,
) ||
record.dialect !== 'postgresql'
) {
throw new TypeError(
'PostgreSQL ModelInvocation migration identity is invalid',
);
}
await client.query(
`INSERT INTO "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."${POSTGRES_MODEL_INVOCATION_MIGRATION_HISTORY_TABLE}"
(migration_id, stream_id, dialect, checksum, applied_at_ms)
VALUES ($1, $2, $3, $4, $5)`,
[
record.migrationId,
record.streamId,
record.dialect,
record.checksum,
record.appliedAtMs,
],
);
},
});
await client.query('COMMIT');
began = false;
return result;
} catch (error) {
if (began) {
try {
await client.query('ROLLBACK');
} catch {
// Preserve the migration failure.
}
}
throw error;
} finally {
client.release();
}
}
}
export const postgresModelInvocationMigrationDefinition: MigrationStreamDefinition<PostgresQueryable> =
Object.freeze({
id: POSTGRES_MODEL_INVOCATION_MIGRATION_STREAM_ID,
dialect: 'postgresql',
migrationIdScheme: 'postgres-prefixed',
checksumScheme: 'sha256',
migrations: Object.freeze([
...postgresCoreMigrations,
...postgresUsagePricingMigrations,
...postgresCatalogMigrations,
...postgresPromptBaseMigrations,
...postgresCredentialMigrations,
...postgresPromptExtensionMigrations,
]),
});
export async function migratePostgresModelInvocationFeature(
pool: PostgresPool,
): Promise<void> {
await runMigrationStream({
stream: postgresModelInvocationMigrationDefinition,
store: new PostgresModelInvocationMigrationStore(pool),
});
}
@@ -0,0 +1,301 @@
import type { PostgresQueryable } from '@qinglong/runtime-core';
import {
POSTGRES_MODEL_PRICE_CATALOG_MIGRATION_ID,
POSTGRES_MODEL_PRICE_CATALOG_AUTHORIZATION_MIGRATION_ID,
POSTGRES_MODEL_INVOCATION_SCHEMA,
} from '../identities';
import { defineSqlMigration } from '../shared';
const POSTGRES_MODEL_PRICE_CATALOG_PUBLICATION_TABLE_SQL = `
CREATE TABLE "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_price_catalog_publications" (
provider varchar(128) NOT NULL,
model varchar(128) NOT NULL,
price_revision varchar(128) NOT NULL,
catalog_digest char(64) NOT NULL,
mutation_id varchar(128) NOT NULL UNIQUE,
command_digest char(64) NOT NULL,
publication_digest char(64) NOT NULL,
published_at_ms bigint NOT NULL,
published_by_user_id varchar(128) NOT NULL,
publication_json jsonb NOT NULL,
PRIMARY KEY (provider, model, price_revision),
CONSTRAINT ql3_model_price_catalog_publication_identity_check CHECK (
provider ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
model ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
price_revision ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
mutation_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
published_by_user_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$'
),
CONSTRAINT ql3_model_price_catalog_publication_value_check CHECK (
published_at_ms >= 0 AND
catalog_digest ~ '^[0-9a-f]{64}$' AND
command_digest ~ '^[0-9a-f]{64}$' AND
publication_digest ~ '^[0-9a-f]{64}$'
),
CONSTRAINT ql3_model_price_catalog_publication_json_check CHECK (
jsonb_typeof(publication_json) = 'object' AND
octet_length(publication_json::text) BETWEEN 2 AND 24576 AND
publication_json @> jsonb_build_object(
'schema', 'qinglong/model-price-catalog-publication@v1',
'mutationId', mutation_id,
'publishedByUserId', published_by_user_id,
'commandDigest', command_digest,
'publicationDigest', publication_digest
) AND
publication_json->'entry' @> jsonb_build_object(
'schema', 'qinglong/model-price-catalog-entry@v1',
'provider', provider,
'model', model,
'priceRevision', price_revision,
'catalogDigest', catalog_digest,
'publishedAtMs', published_at_ms
)
)
)`;
const POSTGRES_MODEL_PRICE_CATALOG_HEAD_TABLE_SQL = `
CREATE TABLE "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_price_catalog_heads" (
provider varchar(128) NOT NULL,
model varchar(128) NOT NULL,
generation integer NOT NULL,
previous_head_digest char(64),
active_price_revision varchar(128),
active_catalog_digest char(64),
revoked_price_revision varchar(128),
revoked_catalog_digest char(64),
action varchar(16) NOT NULL,
mutation_id varchar(128) NOT NULL UNIQUE,
changed_by_user_id varchar(128) NOT NULL,
changed_at_ms bigint NOT NULL,
command_digest char(64) NOT NULL,
head_digest char(64) NOT NULL UNIQUE,
head_json jsonb NOT NULL,
PRIMARY KEY (provider, model, generation),
FOREIGN KEY (previous_head_digest)
REFERENCES "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_price_catalog_heads"
(head_digest) ON DELETE RESTRICT,
FOREIGN KEY (
provider, model, active_price_revision, active_catalog_digest
) REFERENCES "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_price_catalog_publications" (
provider, model, price_revision, catalog_digest
) ON DELETE RESTRICT,
FOREIGN KEY (
provider, model, revoked_price_revision, revoked_catalog_digest
) REFERENCES "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_price_catalog_publications" (
provider, model, price_revision, catalog_digest
) ON DELETE RESTRICT,
CONSTRAINT ql3_model_price_catalog_head_identity_check CHECK (
provider ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
model ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
mutation_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
changed_by_user_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$'
),
CONSTRAINT ql3_model_price_catalog_head_value_check CHECK (
generation BETWEEN 1 AND 2147483647 AND
((generation = 1 AND previous_head_digest IS NULL) OR
(generation > 1 AND previous_head_digest IS NOT NULL)) AND
((active_price_revision IS NULL AND active_catalog_digest IS NULL) OR
(active_price_revision IS NOT NULL AND
active_catalog_digest IS NOT NULL)) AND
((revoked_price_revision IS NULL AND revoked_catalog_digest IS NULL) OR
(revoked_price_revision IS NOT NULL AND
revoked_catalog_digest IS NOT NULL)) AND
(revoked_price_revision IS NULL OR
revoked_price_revision <> active_price_revision) AND
action IN ('activate', 'deactivate', 'revoke') AND
((action = 'revoke') = (revoked_price_revision IS NOT NULL)) AND
changed_at_ms >= 0 AND
(previous_head_digest IS NULL OR
previous_head_digest ~ '^[0-9a-f]{64}$') AND
(active_catalog_digest IS NULL OR
active_catalog_digest ~ '^[0-9a-f]{64}$') AND
(revoked_catalog_digest IS NULL OR
revoked_catalog_digest ~ '^[0-9a-f]{64}$') AND
command_digest ~ '^[0-9a-f]{64}$' AND
head_digest ~ '^[0-9a-f]{64}$'
),
CONSTRAINT ql3_model_price_catalog_head_json_check CHECK (
jsonb_typeof(head_json) = 'object' AND
octet_length(head_json::text) BETWEEN 2 AND 24576 AND
head_json @> jsonb_build_object(
'schema', 'qinglong/model-price-catalog-head@v1',
'provider', provider,
'model', model,
'generation', generation,
'previousHeadDigest', previous_head_digest,
'activePriceRevision', active_price_revision,
'activeCatalogDigest', active_catalog_digest,
'revokedPriceRevision', revoked_price_revision,
'revokedCatalogDigest', revoked_catalog_digest,
'action', action,
'mutationId', mutation_id,
'changedByUserId', changed_by_user_id,
'changedAtMs', changed_at_ms,
'commandDigest', command_digest,
'headDigest', head_digest
)
)
)`;
const postgresModelPriceCatalogMigration =
defineSqlMigration<PostgresQueryable>(
POSTGRES_MODEL_PRICE_CATALOG_MIGRATION_ID,
[
POSTGRES_MODEL_PRICE_CATALOG_PUBLICATION_TABLE_SQL,
`CREATE UNIQUE INDEX ql3_model_price_catalog_publication_identity_digest_uidx
ON "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_price_catalog_publications"
(provider, model, price_revision, catalog_digest)`,
`CREATE UNIQUE INDEX ql3_model_price_catalog_publication_digest_uidx
ON "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_price_catalog_publications"
(publication_digest)`,
POSTGRES_MODEL_PRICE_CATALOG_HEAD_TABLE_SQL,
`CREATE INDEX ql3_model_price_catalog_head_current_idx
ON "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_price_catalog_heads"
(provider, model, generation DESC)`,
`CREATE UNIQUE INDEX ql3_model_price_catalog_revoked_revision_uidx
ON "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_price_catalog_heads"
(provider, model, revoked_price_revision)
WHERE revoked_price_revision IS NOT NULL`,
`GRANT USAGE ON SCHEMA "${POSTGRES_MODEL_INVOCATION_SCHEMA}"
TO ql3_admin`,
`REVOKE ALL ON TABLE
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_price_catalog_publications",
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_price_catalog_heads"
FROM PUBLIC`,
`GRANT SELECT ON TABLE
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_price_catalog_publications",
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_price_catalog_heads"
TO ql3_runtime`,
`GRANT SELECT, INSERT ON TABLE
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_price_catalog_publications",
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_price_catalog_heads"
TO ql3_admin`,
],
(context, statement) => context.query(statement).then(() => undefined),
);
const POSTGRES_MODEL_PRICE_CATALOG_AUTHORIZATION_TABLE_SQL = `
CREATE TABLE "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_price_catalog_authorizations" (
authorization_id varchar(128) PRIMARY KEY,
request_id varchar(128) NOT NULL,
operation varchar(16) NOT NULL,
provider varchar(128) NOT NULL,
model varchar(128) NOT NULL,
price_revision varchar(128),
catalog_command_digest char(64) NOT NULL UNIQUE,
publication_digest char(64) UNIQUE,
head_digest char(64) UNIQUE,
result_digest char(64) NOT NULL UNIQUE,
user_id varchar(128) NOT NULL,
authentication_id varchar(128) NOT NULL,
assurance varchar(32) NOT NULL,
authenticated_at_ms bigint NOT NULL,
expires_at_ms bigint NOT NULL,
policy_revision varchar(128) NOT NULL,
policy_decision_digest char(64) NOT NULL,
decision_mode varchar(32) NOT NULL,
command_digest char(64) NOT NULL UNIQUE,
committed_at_ms bigint NOT NULL,
authorization_digest char(64) NOT NULL UNIQUE,
reasons_json jsonb NOT NULL,
authorization_json jsonb NOT NULL,
FOREIGN KEY (publication_digest)
REFERENCES "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_price_catalog_publications"
(publication_digest) ON DELETE RESTRICT,
FOREIGN KEY (head_digest)
REFERENCES "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_price_catalog_heads"
(head_digest) ON DELETE RESTRICT,
CONSTRAINT ql3_model_price_catalog_authorization_identity_check CHECK (
authorization_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
request_id ~ '^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$' AND
provider ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
model ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
(price_revision IS NULL OR
price_revision ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$') AND
user_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
authentication_id ~ '^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$' AND
policy_revision ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$'
),
CONSTRAINT ql3_model_price_catalog_authorization_value_check CHECK (
operation IN ('publish', 'activate', 'deactivate', 'revoke') AND
((operation = 'deactivate' AND price_revision IS NULL) OR
(operation <> 'deactivate' AND price_revision IS NOT NULL)) AND
((operation = 'publish' AND publication_digest IS NOT NULL AND
head_digest IS NULL AND result_digest = publication_digest) OR
(operation <> 'publish' AND publication_digest IS NULL AND
head_digest IS NOT NULL AND result_digest = head_digest)) AND
assurance IN ('multi_factor', 'hardware', 'local_console') AND
decision_mode IN ('human_confirmation', 'separation_of_duty') AND
authenticated_at_ms >= 0 AND
expires_at_ms > authenticated_at_ms AND
committed_at_ms >= authenticated_at_ms AND
committed_at_ms < expires_at_ms AND
committed_at_ms - authenticated_at_ms <= 300000 AND
catalog_command_digest ~ '^[0-9a-f]{64}$' AND
result_digest ~ '^[0-9a-f]{64}$' AND
policy_decision_digest ~ '^[0-9a-f]{64}$' AND
command_digest ~ '^[0-9a-f]{64}$' AND
authorization_digest ~ '^[0-9a-f]{64}$'
),
CONSTRAINT ql3_model_price_catalog_authorization_reasons_check CHECK (
jsonb_typeof(reasons_json) = 'array' AND
jsonb_array_length(reasons_json) BETWEEN 1 AND 8 AND
octet_length(reasons_json::text) BETWEEN 5 AND 2048
),
CONSTRAINT ql3_model_price_catalog_authorization_json_check CHECK (
jsonb_typeof(authorization_json) = 'object' AND
octet_length(authorization_json::text) BETWEEN 2 AND 32768 AND
authorization_json @> jsonb_build_object(
'schema', 'qinglong/model-price-catalog-authorization@v1',
'authorizationId', authorization_id,
'requestId', request_id,
'operation', operation,
'provider', provider,
'model', model,
'priceRevision', price_revision,
'catalogCommandDigest', catalog_command_digest,
'resultDigest', result_digest,
'decisionMode', decision_mode,
'commandDigest', command_digest,
'committedAtMs', committed_at_ms,
'authorizationDigest', authorization_digest
) AND
authorization_json->'principal' @> jsonb_build_object(
'subject', jsonb_build_object('type', 'user', 'id', user_id),
'authenticationId', authentication_id,
'assurance', assurance,
'authenticatedAtMs', authenticated_at_ms,
'expiresAtMs', expires_at_ms
) AND
authorization_json->'policy' @> jsonb_build_object(
'revision', policy_revision,
'decisionDigest', policy_decision_digest,
'reasons', reasons_json
)
)
)`;
const postgresModelPriceCatalogAuthorizationMigration =
defineSqlMigration<PostgresQueryable>(
POSTGRES_MODEL_PRICE_CATALOG_AUTHORIZATION_MIGRATION_ID,
[
POSTGRES_MODEL_PRICE_CATALOG_AUTHORIZATION_TABLE_SQL,
`CREATE INDEX ql3_model_price_catalog_authorization_target_idx
ON "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_price_catalog_authorizations"
(provider, model, operation, committed_at_ms DESC)`,
`REVOKE ALL ON TABLE
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_price_catalog_authorizations"
FROM PUBLIC`,
`GRANT SELECT, INSERT ON TABLE
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_price_catalog_authorizations"
TO ql3_admin`,
],
(context, statement) => context.query(statement).then(() => undefined),
);
export const postgresCatalogMigrations = Object.freeze([
postgresModelPriceCatalogMigration,
postgresModelPriceCatalogAuthorizationMigration,
]);
@@ -0,0 +1,249 @@
import type { PostgresQueryable } from '@qinglong/runtime-core';
import {
POSTGRES_MODEL_INVOCATION_MIGRATION_ID,
POSTGRES_MODEL_INVOCATION_SCHEMA,
} from '../identities';
import { defineSqlMigration } from '../shared';
const POSTGRES_START_TABLE_SQL = `
CREATE TABLE "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_starts" (
invocation_id varchar(128) PRIMARY KEY,
project_id varchar(128) NOT NULL,
run_id varchar(36) NOT NULL,
step_run_id varchar(128) NOT NULL,
trace_id varchar(128) NOT NULL,
provider varchar(128) NOT NULL,
model varchar(128) NOT NULL,
policy_revision varchar(128) NOT NULL,
request_digest varchar(71) NOT NULL,
input_bytes integer NOT NULL,
max_output_tokens integer NOT NULL,
deadline_at_ms bigint NOT NULL,
admitted_at_ms bigint NOT NULL,
mutation_id varchar(128) NOT NULL,
mutation_digest char(64) NOT NULL,
run_event_id varchar(36) NOT NULL,
start_digest char(64) NOT NULL,
record_json jsonb NOT NULL,
FOREIGN KEY (run_id, step_run_id)
REFERENCES "ql3"."step_runs" (run_id, id) ON DELETE RESTRICT,
FOREIGN KEY (mutation_id)
REFERENCES "ql3"."step_run_mutations" (mutation_id) ON DELETE RESTRICT,
FOREIGN KEY (run_event_id)
REFERENCES "ql3"."run_events" (id) ON DELETE RESTRICT,
CONSTRAINT ql3_model_invocation_starts_identity_check CHECK (
invocation_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
project_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
run_id ~ '^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$' AND
step_run_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
trace_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
provider ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
model ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
policy_revision ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
mutation_id ~ '^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$'
),
CONSTRAINT ql3_model_invocation_starts_value_check CHECK (
request_digest ~ '^sha256:[0-9a-f]{64}$' AND
mutation_digest ~ '^[0-9a-f]{64}$' AND
start_digest ~ '^[0-9a-f]{64}$' AND
input_bytes BETWEEN 1 AND 262144 AND
max_output_tokens BETWEEN 1 AND 32768 AND
admitted_at_ms >= 0 AND deadline_at_ms > admitted_at_ms
),
CONSTRAINT ql3_model_invocation_starts_json_check CHECK (
jsonb_typeof(record_json) = 'object' AND
octet_length(record_json::text) BETWEEN 2 AND 24576 AND
record_json @> jsonb_build_object(
'schema', 'qinglong/model-invocation-start@v1',
'invocationId', invocation_id,
'projectId', project_id,
'runId', run_id,
'stepRunId', step_run_id,
'traceId', trace_id,
'provider', provider,
'model', model,
'policyRevision', policy_revision,
'requestDigest', request_digest,
'inputBytes', input_bytes,
'maxOutputTokens', max_output_tokens,
'deadlineAtMs', deadline_at_ms,
'admittedAtMs', admitted_at_ms,
'stepRunMutationId', mutation_id,
'stepRunMutationDigest', mutation_digest,
'runEventId', run_event_id,
'startDigest', start_digest
)
)
)`;
const POSTGRES_COMPLETION_TABLE_SQL = `
CREATE TABLE "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_completions" (
invocation_id varchar(128) PRIMARY KEY,
project_id varchar(128) NOT NULL,
run_id varchar(36) NOT NULL,
step_run_id varchar(128) NOT NULL,
trace_id varchar(128) NOT NULL,
start_digest char(64) NOT NULL,
outcome varchar(32) NOT NULL,
output_bytes integer NOT NULL,
error_code varchar(64),
completed_at_ms bigint NOT NULL,
mutation_id varchar(128) NOT NULL,
mutation_digest char(64) NOT NULL,
run_event_id varchar(36) NOT NULL,
completion_digest char(64) NOT NULL,
record_json jsonb NOT NULL,
FOREIGN KEY (invocation_id)
REFERENCES "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_starts" (invocation_id)
ON DELETE RESTRICT,
FOREIGN KEY (run_id, step_run_id)
REFERENCES "ql3"."step_runs" (run_id, id) ON DELETE RESTRICT,
FOREIGN KEY (mutation_id)
REFERENCES "ql3"."step_run_mutations" (mutation_id) ON DELETE RESTRICT,
FOREIGN KEY (run_event_id)
REFERENCES "ql3"."run_events" (id) ON DELETE RESTRICT,
CONSTRAINT ql3_model_invocation_completions_value_check CHECK (
outcome IN ('succeeded', 'failed', 'timed_out', 'outcome_unknown') AND
output_bytes BETWEEN 0 AND 1048576 AND completed_at_ms >= 0 AND
((outcome = 'succeeded' AND error_code IS NULL) OR
(outcome <> 'succeeded' AND
error_code ~ '^[A-Z][A-Z0-9_]{0,63}$')) AND
start_digest ~ '^[0-9a-f]{64}$' AND
mutation_digest ~ '^[0-9a-f]{64}$' AND
completion_digest ~ '^[0-9a-f]{64}$'
),
CONSTRAINT ql3_model_invocation_completions_json_check CHECK (
jsonb_typeof(record_json) = 'object' AND
octet_length(record_json::text) BETWEEN 2 AND 24576 AND
record_json @> jsonb_build_object(
'schema', 'qinglong/model-invocation-completion@v1',
'invocationId', invocation_id,
'projectId', project_id,
'runId', run_id,
'stepRunId', step_run_id,
'traceId', trace_id,
'startDigest', start_digest,
'outcome', outcome,
'outputBytes', output_bytes,
'errorCode', error_code,
'completedAtMs', completed_at_ms,
'stepRunMutationId', mutation_id,
'stepRunMutationDigest', mutation_digest,
'runEventId', run_event_id,
'completionDigest', completion_digest
)
)
)`;
const POSTGRES_RESOLUTION_TABLE_SQL = `
CREATE TABLE "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_resolutions" (
resolution_id varchar(128) PRIMARY KEY,
invocation_id varchar(128) NOT NULL UNIQUE,
project_id varchar(128) NOT NULL,
run_id varchar(36) NOT NULL,
step_run_id varchar(128) NOT NULL,
trace_id varchar(128) NOT NULL,
completion_digest char(64) NOT NULL,
decision varchar(16) NOT NULL,
resolved_by_user_id varchar(128) NOT NULL,
resolved_at_ms bigint NOT NULL,
mutation_id varchar(128) NOT NULL UNIQUE,
mutation_digest char(64) NOT NULL,
run_event_id varchar(36) NOT NULL UNIQUE,
resolution_digest char(64) NOT NULL UNIQUE,
record_json jsonb NOT NULL,
FOREIGN KEY (invocation_id)
REFERENCES "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_completions" (invocation_id)
ON DELETE RESTRICT,
FOREIGN KEY (run_id, step_run_id)
REFERENCES "ql3"."step_runs" (run_id, id) ON DELETE RESTRICT,
FOREIGN KEY (mutation_id)
REFERENCES "ql3"."step_run_mutations" (mutation_id) ON DELETE RESTRICT,
FOREIGN KEY (run_event_id)
REFERENCES "ql3"."run_events" (id) ON DELETE RESTRICT,
CONSTRAINT ql3_model_invocation_resolutions_value_check CHECK (
decision IN ('retry', 'fail', 'cancel') AND
resolved_at_ms >= 0 AND
resolution_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
invocation_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
project_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
run_id ~ '^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$' AND
step_run_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
trace_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
resolved_by_user_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
mutation_id ~ '^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$' AND
completion_digest ~ '^[0-9a-f]{64}$' AND
mutation_digest ~ '^[0-9a-f]{64}$' AND
resolution_digest ~ '^[0-9a-f]{64}$'
),
CONSTRAINT ql3_model_invocation_resolutions_json_check CHECK (
jsonb_typeof(record_json) = 'object' AND
octet_length(record_json::text) BETWEEN 2 AND 24576 AND
record_json @> jsonb_build_object(
'schema', 'qinglong/model-invocation-resolution@v1',
'resolutionId', resolution_id,
'invocationId', invocation_id,
'projectId', project_id,
'runId', run_id,
'stepRunId', step_run_id,
'traceId', trace_id,
'completionDigest', completion_digest,
'decision', decision,
'resolvedByUserId', resolved_by_user_id,
'resolvedAtMs', resolved_at_ms,
'stepRunMutationId', mutation_id,
'stepRunMutationDigest', mutation_digest,
'runEventId', run_event_id,
'resolutionDigest', resolution_digest
)
)
)`;
const postgresMigration = defineSqlMigration<PostgresQueryable>(
POSTGRES_MODEL_INVOCATION_MIGRATION_ID,
[
POSTGRES_START_TABLE_SQL,
`CREATE INDEX ql3_model_invocation_starts_step_history_idx
ON "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_starts"
(run_id, step_run_id, admitted_at_ms, invocation_id)`,
`CREATE UNIQUE INDEX ql3_model_invocation_starts_mutation_uidx
ON "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_starts" (mutation_id)`,
`CREATE UNIQUE INDEX ql3_model_invocation_starts_event_uidx
ON "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_starts" (run_event_id)`,
`CREATE UNIQUE INDEX ql3_model_invocation_starts_digest_uidx
ON "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_starts" (start_digest)`,
POSTGRES_COMPLETION_TABLE_SQL,
`CREATE INDEX ql3_model_invocation_completions_step_history_idx
ON "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_completions"
(run_id, step_run_id, completed_at_ms, invocation_id)`,
`CREATE UNIQUE INDEX ql3_model_invocation_completions_mutation_uidx
ON "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_completions" (mutation_id)`,
`CREATE UNIQUE INDEX ql3_model_invocation_completions_event_uidx
ON "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_completions" (run_event_id)`,
`CREATE UNIQUE INDEX ql3_model_invocation_completions_digest_uidx
ON "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_completions" (completion_digest)`,
POSTGRES_RESOLUTION_TABLE_SQL,
`CREATE INDEX ql3_model_invocation_resolutions_step_history_idx
ON "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_resolutions"
(run_id, step_run_id, resolved_at_ms, resolution_id)`,
`GRANT USAGE ON SCHEMA "${POSTGRES_MODEL_INVOCATION_SCHEMA}"
TO ql3_runtime`,
`REVOKE ALL ON TABLE
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_starts",
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_completions",
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_resolutions"
FROM PUBLIC`,
`GRANT SELECT, INSERT ON TABLE
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_starts",
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_completions",
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_resolutions"
TO ql3_runtime`,
],
(context, statement) => context.query(statement).then(() => undefined),
);
export const postgresCoreMigrations = Object.freeze([
postgresMigration,
]);
@@ -0,0 +1,513 @@
import type { PostgresQueryable } from '@qinglong/runtime-core';
import {
POSTGRES_MODEL_PROVIDER_CREDENTIAL_CATALOG_MIGRATION_ID,
POSTGRES_MODEL_PROVIDER_CREDENTIAL_MANAGEMENT_MIGRATION_ID,
POSTGRES_MODEL_PROVIDER_CREDENTIAL_MANAGEMENT_IDENTITY_MIGRATION_ID,
POSTGRES_MODEL_PROVIDER_CREDENTIAL_TEST_CONNECTION_MIGRATION_ID,
POSTGRES_MODEL_INVOCATION_SCHEMA,
} from '../identities';
import { defineSqlMigration } from '../shared';
const POSTGRES_MODEL_PROVIDER_CREDENTIAL_BINDING_TABLE_SQL = `
CREATE TABLE "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_provider_credential_bindings" (
project_id varchar(128) NOT NULL,
provider varchar(128) NOT NULL,
revision varchar(128) NOT NULL,
secret_ref varchar(512) NOT NULL,
scheme varchar(16) NOT NULL,
binding_digest varchar(71) NOT NULL UNIQUE,
binding_json jsonb NOT NULL,
PRIMARY KEY (project_id, provider, revision),
CONSTRAINT ql3_ai_model_provider_credential_binding_project_fk
FOREIGN KEY (project_id) REFERENCES "ql3"."projects" (id)
ON DELETE RESTRICT,
CONSTRAINT ql3_ai_model_provider_credential_binding_identity_check CHECK (
project_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
provider ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
revision ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
length(secret_ref) BETWEEN 1 AND 512 AND
secret_ref LIKE 'qlsecret:v1:%' AND
scheme = 'bearer' AND
binding_digest ~ '^sha256:[0-9a-f]{64}$'
),
CONSTRAINT ql3_ai_model_provider_credential_binding_json_check CHECK (
jsonb_typeof(binding_json) = 'object' AND
octet_length(binding_json::text) BETWEEN 2 AND 4096 AND
binding_json @> jsonb_build_object(
'schema', 'qinglong/model-provider-credential-binding@v1',
'projectId', project_id, 'provider', provider,
'revision', revision, 'secretRef', secret_ref, 'scheme', scheme
)
)
)`;
const POSTGRES_MODEL_PROVIDER_CREDENTIAL_TRANSITION_TABLE_SQL = `
CREATE TABLE "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_provider_credential_transitions" (
project_id varchar(128) NOT NULL,
provider varchar(128) NOT NULL,
generation integer NOT NULL,
action varchar(16) NOT NULL,
active_binding_revision varchar(128),
active_binding_digest char(64),
previous_transition_digest char(64),
mutation_id varchar(128) NOT NULL UNIQUE,
changed_by_type varchar(32) NOT NULL,
changed_by_id varchar(128) NOT NULL,
changed_at_ms bigint NOT NULL,
command_digest char(64) NOT NULL,
transition_digest char(64) NOT NULL UNIQUE,
transition_json jsonb NOT NULL,
PRIMARY KEY (project_id, provider, generation),
CONSTRAINT ql3_ai_model_provider_credential_transition_project_fk
FOREIGN KEY (project_id) REFERENCES "ql3"."projects" (id)
ON DELETE RESTRICT,
CONSTRAINT ql3_ai_model_provider_credential_transition_binding_fk
FOREIGN KEY (project_id, provider, active_binding_revision)
REFERENCES "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_provider_credential_bindings"
(project_id, provider, revision) ON DELETE RESTRICT,
CONSTRAINT ql3_ai_model_provider_credential_transition_identity_check CHECK (
project_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
provider ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
generation BETWEEN 1 AND 2147483647 AND
action IN ('bind', 'revoke') AND
mutation_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
changed_by_type IN ('user', 'api_app', 'mcp_client', 'agent', 'system', 'worker') AND
changed_by_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
changed_at_ms >= 0
),
CONSTRAINT ql3_ai_model_provider_credential_transition_state_check CHECK (
(action = 'bind' AND active_binding_revision IS NOT NULL AND
active_binding_digest ~ '^[0-9a-f]{64}$') OR
(action = 'revoke' AND active_binding_revision IS NULL AND
active_binding_digest IS NULL)
),
CONSTRAINT ql3_ai_model_provider_credential_transition_digest_check CHECK (
(previous_transition_digest IS NULL OR
previous_transition_digest ~ '^[0-9a-f]{64}$') AND
command_digest ~ '^[0-9a-f]{64}$' AND
transition_digest ~ '^[0-9a-f]{64}$'
),
CONSTRAINT ql3_ai_model_provider_credential_transition_json_check CHECK (
jsonb_typeof(transition_json) = 'object' AND
octet_length(transition_json::text) BETWEEN 2 AND 8192 AND
transition_json @> jsonb_build_object(
'schema', 'qinglong/model-provider-credential-transition@v1',
'mutationId', mutation_id, 'projectId', project_id,
'provider', provider, 'generation', generation, 'action', action,
'activeBindingRevision', active_binding_revision,
'activeBindingDigest', active_binding_digest,
'previousTransitionDigest', previous_transition_digest,
'changedAtMs', changed_at_ms, 'commandDigest', command_digest,
'transitionDigest', transition_digest
) AND
transition_json -> 'changedBy' @> jsonb_build_object(
'type', changed_by_type, 'id', changed_by_id
)
)
)`;
const POSTGRES_MODEL_PROVIDER_CREDENTIAL_AUDIT_TABLE_SQL = `
CREATE TABLE "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_provider_credential_audits" (
project_id varchar(128) NOT NULL,
provider varchar(128) NOT NULL,
request_id varchar(128) NOT NULL,
operation varchar(16) NOT NULL,
binding_revision varchar(128) NOT NULL,
binding_digest varchar(71) NOT NULL,
occurred_at_ms bigint NOT NULL,
audit_digest char(64) PRIMARY KEY,
audit_json jsonb NOT NULL,
CONSTRAINT ql3_ai_model_provider_credential_audit_identity_uidx
UNIQUE (project_id, provider, request_id, operation),
CONSTRAINT ql3_ai_model_provider_credential_audit_project_fk
FOREIGN KEY (project_id) REFERENCES "ql3"."projects" (id)
ON DELETE RESTRICT,
CONSTRAINT ql3_ai_model_provider_credential_audit_identity_check CHECK (
project_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
provider ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
request_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
operation IN ('list_models', 'generate', 'stream') AND
binding_revision ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
binding_digest ~ '^sha256:[0-9a-f]{64}$' AND
occurred_at_ms >= 0 AND audit_digest ~ '^[0-9a-f]{64}$'
),
CONSTRAINT ql3_ai_model_provider_credential_audit_json_check CHECK (
jsonb_typeof(audit_json) = 'object' AND
octet_length(audit_json::text) BETWEEN 2 AND 4096 AND
audit_json @> jsonb_build_object(
'schema', 'qinglong/model-provider-credential-audit@v1',
'projectId', project_id, 'provider', provider,
'requestId', request_id, 'operation', operation,
'bindingRevision', binding_revision,
'bindingDigest', binding_digest, 'occurredAtMs', occurred_at_ms
)
)
)`;
const postgresModelProviderCredentialCatalogMigration =
defineSqlMigration<PostgresQueryable>(
POSTGRES_MODEL_PROVIDER_CREDENTIAL_CATALOG_MIGRATION_ID,
[
POSTGRES_MODEL_PROVIDER_CREDENTIAL_BINDING_TABLE_SQL,
POSTGRES_MODEL_PROVIDER_CREDENTIAL_TRANSITION_TABLE_SQL,
POSTGRES_MODEL_PROVIDER_CREDENTIAL_AUDIT_TABLE_SQL,
`CREATE INDEX ql3_ai_model_provider_credential_transition_current_idx
ON "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_provider_credential_transitions"
(project_id, provider, generation DESC)`,
`CREATE INDEX ql3_ai_model_provider_credential_audit_project_time_idx
ON "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_provider_credential_audits"
(project_id, occurred_at_ms DESC, request_id)`,
`REVOKE ALL ON TABLE
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_provider_credential_bindings",
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_provider_credential_transitions",
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_provider_credential_audits"
FROM PUBLIC`,
`GRANT USAGE ON SCHEMA "${POSTGRES_MODEL_INVOCATION_SCHEMA}"
TO ql3_runtime, ql3_ai_maintenance`,
`GRANT SELECT ON TABLE
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_provider_credential_bindings",
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_provider_credential_transitions"
TO ql3_runtime`,
`GRANT SELECT, INSERT ON TABLE
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_provider_credential_audits"
TO ql3_runtime`,
`GRANT SELECT, INSERT ON TABLE
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_provider_credential_bindings",
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_provider_credential_transitions"
TO ql3_ai_maintenance`,
`GRANT SELECT ON TABLE
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_provider_credential_audits"
TO ql3_ai_maintenance`,
],
(context, statement) => context.query(statement).then(() => undefined),
);
const postgresModelProviderCredentialManagementMigration =
defineSqlMigration<PostgresQueryable>(
POSTGRES_MODEL_PROVIDER_CREDENTIAL_MANAGEMENT_MIGRATION_ID,
[
`DO $ql3_ai$
BEGIN
EXECUTE format(
'GRANT CONNECT ON DATABASE %I TO ql3_ai_credential_manager',
current_database()
);
END
$ql3_ai$`,
`REVOKE CREATE ON SCHEMA "ql3", "${POSTGRES_MODEL_INVOCATION_SCHEMA}"
FROM ql3_ai_credential_manager`,
`REVOKE ALL ON ALL TABLES IN SCHEMA "${POSTGRES_MODEL_INVOCATION_SCHEMA}"
FROM ql3_ai_credential_manager`,
`REVOKE ALL ON TABLE
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_provider_credential_bindings",
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_provider_credential_transitions",
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_provider_credential_audits"
FROM ql3_ai_maintenance`,
`REVOKE ALL ON TABLE
"ql3"."projects",
"ql3"."project_role_bindings",
"ql3"."security_audit_events"
FROM ql3_ai_credential_manager`,
`GRANT USAGE ON SCHEMA "ql3", "${POSTGRES_MODEL_INVOCATION_SCHEMA}"
TO ql3_ai_credential_manager`,
`GRANT SELECT ON TABLE
"ql3"."projects",
"ql3"."project_role_bindings",
"ql3"."security_audit_events"
TO ql3_ai_credential_manager`,
`GRANT INSERT ON TABLE "ql3"."security_audit_events"
TO ql3_ai_credential_manager`,
`GRANT SELECT ON TABLE
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."ai_schema_migrations",
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_provider_credential_bindings",
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_provider_credential_transitions",
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_provider_credential_audits"
TO ql3_ai_credential_manager`,
`GRANT INSERT ON TABLE
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_provider_credential_bindings",
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_provider_credential_transitions"
TO ql3_ai_credential_manager`,
],
(context, statement) => context.query(statement).then(() => undefined),
);
const postgresModelProviderCredentialManagementIdentityMigration =
defineSqlMigration<PostgresQueryable>(
POSTGRES_MODEL_PROVIDER_CREDENTIAL_MANAGEMENT_IDENTITY_MIGRATION_ID,
[
`CREATE TABLE "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_provider_credential_management_identity_keyset_ledger" (
authority varchar(64) PRIMARY KEY,
generation bigint NOT NULL,
digest varchar(43) NOT NULL,
issuer varchar(512) NOT NULL,
audience varchar(256) NOT NULL,
active_key_ids jsonb NOT NULL,
revoked_key_ids jsonb NOT NULL,
updated_at_ms bigint NOT NULL,
CONSTRAINT ql3_ai_provider_credential_identity_authority_check
CHECK (authority = 'model-provider-credential-management'),
CONSTRAINT ql3_ai_provider_credential_identity_generation_check
CHECK (generation >= 1 AND updated_at_ms >= 0),
CONSTRAINT ql3_ai_provider_credential_identity_digest_check
CHECK (digest ~ '^[A-Za-z0-9_-]{43}$'),
CONSTRAINT ql3_ai_provider_credential_identity_trust_domain_check
CHECK (
char_length(issuer) BETWEEN 1 AND 512 AND
issuer !~ '[[:cntrl:]]' AND
char_length(audience) BETWEEN 1 AND 256 AND
audience !~ '[[:cntrl:]]'
),
CONSTRAINT ql3_ai_provider_credential_identity_keys_check
CHECK (
jsonb_typeof(active_key_ids) = 'array' AND
jsonb_array_length(active_key_ids) BETWEEN 1 AND 8 AND
octet_length(active_key_ids::text) BETWEEN 3 AND 8192 AND
jsonb_typeof(revoked_key_ids) = 'array' AND
jsonb_array_length(revoked_key_ids) BETWEEN 0 AND 64 AND
octet_length(revoked_key_ids::text) BETWEEN 2 AND 16384
)
)`,
`REVOKE ALL ON TABLE
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_provider_credential_management_identity_keyset_ledger"
FROM PUBLIC, ql3_ai_maintenance`,
`GRANT SELECT, INSERT, UPDATE ON TABLE
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_provider_credential_management_identity_keyset_ledger"
TO ql3_ai_credential_manager`,
],
(context, statement) => context.query(statement).then(() => undefined),
);
const postgresModelProviderCredentialTestConnectionMigration =
defineSqlMigration<PostgresQueryable>(
POSTGRES_MODEL_PROVIDER_CREDENTIAL_TEST_CONNECTION_MIGRATION_ID,
[
`CREATE TABLE "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_provider_credential_test_plans" (
test_id uuid PRIMARY KEY,
request_id varchar(128) NOT NULL,
project_id varchar(128) NOT NULL,
provider varchar(128) NOT NULL,
adapter varchar(32) NOT NULL,
base_url varchar(1024) NOT NULL,
endpoint_revision varchar(128) NOT NULL,
endpoint_config_digest char(64) NOT NULL,
deadline_ms integer NOT NULL,
max_response_bytes integer NOT NULL,
max_models integer NOT NULL,
max_cost_microusd bigint NOT NULL,
retry_limit integer NOT NULL,
requested_by_type varchar(32) NOT NULL,
requested_by_id varchar(128) NOT NULL,
project_version integer NOT NULL,
binding_version integer NOT NULL,
planned_at_ms bigint NOT NULL,
expires_at_ms bigint NOT NULL,
plan_digest char(64) NOT NULL UNIQUE,
plan_json jsonb NOT NULL,
CONSTRAINT ql3_ai_provider_credential_test_plan_project_fk
FOREIGN KEY (project_id) REFERENCES "ql3"."projects" (id)
ON DELETE RESTRICT,
CONSTRAINT ql3_ai_provider_credential_test_plan_identity_uidx
UNIQUE (project_id, request_id),
CONSTRAINT ql3_ai_provider_credential_test_plan_digest_uidx
UNIQUE (test_id, plan_digest),
CONSTRAINT ql3_ai_provider_credential_test_plan_identity_check CHECK (
project_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
provider ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
request_id ~ '^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$' AND
endpoint_revision ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
requested_by_type = 'user' AND
char_length(requested_by_id) BETWEEN 1 AND 128 AND
requested_by_id !~ '[[:cntrl:]]'
),
CONSTRAINT ql3_ai_provider_credential_test_plan_budget_check CHECK (
adapter = 'openai-compatible' AND
base_url ~ '^https://[^[:cntrl:]]+/$' AND
endpoint_config_digest ~ '^[0-9a-f]{64}$' AND
deadline_ms BETWEEN 1000 AND 15000 AND
max_response_bytes BETWEEN 1024 AND 262144 AND
max_models BETWEEN 1 AND 256 AND
max_cost_microusd = 0 AND retry_limit = 0
),
CONSTRAINT ql3_ai_provider_credential_test_plan_fence_check CHECK (
project_version BETWEEN 1 AND 2147483647 AND
binding_version BETWEEN 1 AND 2147483647 AND
planned_at_ms >= 0 AND expires_at_ms > planned_at_ms AND
expires_at_ms - planned_at_ms <= 300000 AND
plan_digest ~ '^[0-9a-f]{64}$'
),
CONSTRAINT ql3_ai_provider_credential_test_plan_json_check CHECK (
jsonb_typeof(plan_json) = 'object' AND
octet_length(plan_json::text) BETWEEN 2 AND 16384 AND
plan_json @> jsonb_build_object(
'schema', 'qinglong/model-provider-credential-test-plan@v1',
'testId', test_id::text, 'requestId', request_id,
'projectId', project_id, 'provider', provider,
'plannedAtMs', planned_at_ms, 'expiresAtMs', expires_at_ms,
'planDigest', plan_digest
) AND
plan_json -> 'endpoint' @> jsonb_build_object(
'provider', provider, 'adapter', adapter, 'baseUrl', base_url,
'revision', endpoint_revision,
'configDigest', endpoint_config_digest,
'deadlineMs', deadline_ms,
'maxResponseBytes', max_response_bytes,
'maxModels', max_models, 'maxCostMicrousd', max_cost_microusd,
'retryLimit', retry_limit
) AND
plan_json -> 'requestedBy' @> jsonb_build_object(
'type', requested_by_type, 'id', requested_by_id
) AND
plan_json -> 'fence' @> jsonb_build_object(
'projectVersion', project_version,
'bindingVersion', binding_version
)
)
)`,
`CREATE INDEX ql3_ai_provider_credential_test_plan_expiry_idx
ON "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_provider_credential_test_plans"
(expires_at_ms, test_id)`,
`CREATE TABLE "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_provider_credential_test_quota_buckets" (
project_id varchar(128) NOT NULL,
subject_id varchar(128) NOT NULL,
window_started_at_ms bigint NOT NULL,
consumed_count integer NOT NULL,
receipt_ids jsonb NOT NULL,
updated_at_ms bigint NOT NULL,
PRIMARY KEY (project_id, subject_id),
CONSTRAINT ql3_ai_provider_credential_test_quota_project_fk
FOREIGN KEY (project_id) REFERENCES "ql3"."projects" (id)
ON DELETE RESTRICT,
CONSTRAINT ql3_ai_provider_credential_test_quota_identity_check CHECK (
project_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
char_length(subject_id) BETWEEN 1 AND 128 AND
subject_id !~ '[[:cntrl:]]'
),
CONSTRAINT ql3_ai_provider_credential_test_quota_value_check CHECK (
window_started_at_ms >= 0 AND
consumed_count BETWEEN 1 AND 32 AND
updated_at_ms >= window_started_at_ms AND
jsonb_typeof(receipt_ids) = 'array' AND
jsonb_array_length(receipt_ids) BETWEEN 1 AND 32 AND
octet_length(receipt_ids::text) BETWEEN 3 AND 8192
)
)`,
`CREATE TABLE "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_provider_credential_test_executions" (
execution_id uuid PRIMARY KEY,
test_id uuid NOT NULL UNIQUE,
plan_digest char(64) NOT NULL,
started_at_ms bigint NOT NULL,
execution_digest char(64) NOT NULL UNIQUE,
execution_json jsonb NOT NULL,
CONSTRAINT ql3_ai_provider_credential_test_execution_plan_fk
FOREIGN KEY (test_id, plan_digest)
REFERENCES "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_provider_credential_test_plans"
(test_id, plan_digest) ON DELETE RESTRICT,
CONSTRAINT ql3_ai_provider_credential_test_execution_value_check CHECK (
plan_digest ~ '^[0-9a-f]{64}$' AND started_at_ms >= 0 AND
execution_digest ~ '^[0-9a-f]{64}$'
),
CONSTRAINT ql3_ai_provider_credential_test_execution_json_check CHECK (
jsonb_typeof(execution_json) = 'object' AND
octet_length(execution_json::text) BETWEEN 2 AND 4096 AND
execution_json @> jsonb_build_object(
'schema', 'qinglong/model-provider-credential-test-execution@v1',
'executionId', execution_id::text, 'testId', test_id::text,
'planDigest', plan_digest, 'startedAtMs', started_at_ms,
'executionDigest', execution_digest
)
)
)`,
`ALTER TABLE "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_provider_credential_test_executions"
ADD CONSTRAINT ql3_ai_provider_credential_test_execution_identity_uidx
UNIQUE (execution_id, test_id, plan_digest)`,
`CREATE TABLE "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_provider_credential_test_results" (
execution_id uuid PRIMARY KEY,
test_id uuid NOT NULL UNIQUE,
plan_digest char(64) NOT NULL,
outcome varchar(16) NOT NULL,
model_count integer,
duration_ms integer NOT NULL,
completed_at_ms bigint NOT NULL,
result_digest char(64) NOT NULL UNIQUE,
result_json jsonb NOT NULL,
CONSTRAINT ql3_ai_provider_credential_test_result_execution_fk
FOREIGN KEY (execution_id, test_id, plan_digest)
REFERENCES "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_provider_credential_test_executions"
(execution_id, test_id, plan_digest) ON DELETE RESTRICT,
CONSTRAINT ql3_ai_provider_credential_test_result_value_check CHECK (
plan_digest ~ '^[0-9a-f]{64}$' AND
((outcome = 'reachable' AND model_count BETWEEN 0 AND 256) OR
(outcome = 'unreachable' AND model_count IS NULL)) AND
duration_ms BETWEEN 0 AND 15000 AND completed_at_ms >= 0 AND
result_digest ~ '^[0-9a-f]{64}$'
),
CONSTRAINT ql3_ai_provider_credential_test_result_json_check CHECK (
jsonb_typeof(result_json) = 'object' AND
octet_length(result_json::text) BETWEEN 2 AND 4096 AND
result_json @> jsonb_build_object(
'schema', 'qinglong/model-provider-credential-test-result@v1',
'executionId', execution_id::text, 'testId', test_id::text,
'planDigest', plan_digest, 'outcome', outcome,
'modelCount', model_count, 'durationMs', duration_ms,
'completedAtMs', completed_at_ms, 'resultDigest', result_digest
)
)
)`,
`DO $ql3_ai$
BEGIN
EXECUTE format(
'GRANT CONNECT ON DATABASE %I TO ql3_ai_credential_tester',
current_database()
);
END
$ql3_ai$`,
`REVOKE CREATE ON SCHEMA "ql3", "${POSTGRES_MODEL_INVOCATION_SCHEMA}"
FROM ql3_ai_credential_tester`,
`REVOKE ALL ON ALL TABLES IN SCHEMA "ql3", "${POSTGRES_MODEL_INVOCATION_SCHEMA}"
FROM ql3_ai_credential_tester`,
`REVOKE ALL ON TABLE
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_provider_credential_test_plans",
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_provider_credential_test_quota_buckets",
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_provider_credential_test_executions",
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_provider_credential_test_results"
FROM PUBLIC, ql3_runtime, ql3_ai_maintenance, ql3_admin,
ql3_package_manager, ql3_package_executor, ql3_worker_ingress,
ql3_ai_credential_manager, ql3_ai_credential_tester`,
`GRANT USAGE ON SCHEMA "${POSTGRES_MODEL_INVOCATION_SCHEMA}"
TO ql3_ai_credential_tester`,
`GRANT SELECT, INSERT ON TABLE
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_provider_credential_test_plans"
TO ql3_ai_credential_manager`,
`GRANT SELECT, INSERT, UPDATE ON TABLE
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_provider_credential_test_quota_buckets"
TO ql3_ai_credential_manager`,
`GRANT SELECT ON TABLE
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_provider_credential_test_executions",
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_provider_credential_test_results"
TO ql3_ai_credential_manager`,
`GRANT SELECT ON TABLE
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."ai_schema_migrations",
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_provider_credential_bindings",
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_provider_credential_transitions",
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_provider_credential_audits",
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_provider_credential_test_plans",
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_provider_credential_test_executions",
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_provider_credential_test_results"
TO ql3_ai_credential_tester`,
`GRANT INSERT ON TABLE
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_provider_credential_audits",
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_provider_credential_test_executions",
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_provider_credential_test_results"
TO ql3_ai_credential_tester`,
],
(context, statement) => context.query(statement).then(() => undefined),
);
export const postgresCredentialMigrations = Object.freeze([
postgresModelProviderCredentialCatalogMigration,
postgresModelProviderCredentialManagementMigration,
postgresModelProviderCredentialManagementIdentityMigration,
postgresModelProviderCredentialTestConnectionMigration,
]);
@@ -0,0 +1,855 @@
import type { PostgresQueryable } from '@qinglong/runtime-core';
import {
POSTGRES_PLUGIN_PACKAGE_PROMPT_ADMISSION_MIGRATION_ID,
POSTGRES_PLUGIN_PACKAGE_PROMPT_FINALIZATION_MIGRATION_ID,
POSTGRES_PLUGIN_PACKAGE_PROMPT_OUTPUT_ARTIFACT_MIGRATION_ID,
POSTGRES_PLUGIN_PACKAGE_PROMPT_OUTPUT_TOMBSTONE_MIGRATION_ID,
POSTGRES_PLUGIN_PACKAGE_PROMPT_OUTPUT_KEY_RETIREMENT_MIGRATION_ID,
POSTGRES_PLUGIN_PACKAGE_PROMPT_OUTPUT_KEY_ROTATION_MIGRATION_ID,
POSTGRES_PLUGIN_PACKAGE_PROMPT_PRODUCT_AUTHORIZATION_MIGRATION_ID,
POSTGRES_MODEL_INVOCATION_SCHEMA,
POSTGRES_MODEL_INVOCATION_MIGRATION_HISTORY_TABLE,
} from '../identities';
import { defineSqlMigration } from '../shared';
const POSTGRES_PLUGIN_PACKAGE_PROMPT_ADMISSION_TABLE_SQL = `
CREATE TABLE "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_prompt_admissions" (
request_id varchar(128) PRIMARY KEY,
invocation_id varchar(128) NOT NULL UNIQUE,
plan_digest char(64) NOT NULL UNIQUE,
run_id varchar(36) NOT NULL UNIQUE,
step_run_id varchar(128) NOT NULL UNIQUE,
project_id varchar(128) NOT NULL,
package_name varchar(63) NOT NULL,
installation_id varchar(128) NOT NULL,
lock_digest char(64) NOT NULL,
generation integer NOT NULL,
generation_digest char(64) NOT NULL,
materialized_revision_digest char(64) NOT NULL,
publication_digest char(64) NOT NULL,
prompt_id varchar(128) NOT NULL,
prompt_definition_digest char(64) NOT NULL,
parameter_digest char(64) NOT NULL,
model_request_digest varchar(71) NOT NULL,
admitted_at_ms bigint NOT NULL,
receipt_digest char(64) NOT NULL UNIQUE,
plan_json jsonb NOT NULL,
receipt_json jsonb NOT NULL,
CONSTRAINT ql3_ai_prompt_admission_run_fk
FOREIGN KEY (run_id) REFERENCES "ql3"."runs" (id) ON DELETE RESTRICT,
CONSTRAINT ql3_ai_prompt_admission_step_fk
FOREIGN KEY (run_id, step_run_id)
REFERENCES "ql3"."step_runs" (run_id, id) ON DELETE RESTRICT,
CONSTRAINT ql3_ai_prompt_admission_publication_fk
FOREIGN KEY (publication_digest)
REFERENCES "ql3"."plugin_package_automation_publications" (
publication_digest
) ON DELETE RESTRICT,
CONSTRAINT ql3_ai_prompt_admission_identity_check CHECK (
request_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
invocation_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
run_id ~ '^[A-Za-z0-9][A-Za-z0-9._:-]{0,35}$' AND
step_run_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
project_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
package_name ~ '^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$' AND
installation_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
prompt_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
generation BETWEEN 1 AND 2147483647 AND admitted_at_ms >= 0
),
CONSTRAINT ql3_ai_prompt_admission_digest_check CHECK (
plan_digest ~ '^[0-9a-f]{64}$' AND
lock_digest ~ '^[0-9a-f]{64}$' AND
generation_digest ~ '^[0-9a-f]{64}$' AND
materialized_revision_digest ~ '^[0-9a-f]{64}$' AND
publication_digest ~ '^[0-9a-f]{64}$' AND
prompt_definition_digest ~ '^[0-9a-f]{64}$' AND
parameter_digest ~ '^[0-9a-f]{64}$' AND
model_request_digest ~ '^sha256:[0-9a-f]{64}$' AND
receipt_digest ~ '^[0-9a-f]{64}$'
),
CONSTRAINT ql3_ai_prompt_admission_json_check CHECK (
jsonb_typeof(plan_json) = 'object' AND
octet_length(plan_json::text) BETWEEN 2 AND 32768 AND
plan_json @> jsonb_build_object(
'schema', 'qinglong/plugin-package-prompt-execution-plan@v1',
'requestId', request_id, 'invocationId', invocation_id,
'planDigest', plan_digest, 'runId', run_id, 'stepRunId', step_run_id,
'parameterDigest', parameter_digest,
'modelRequestDigest', model_request_digest,
'plannedAtMs', admitted_at_ms
) AND
plan_json -> 'target' @> jsonb_build_object(
'projectId', project_id, 'packageName', package_name,
'installationId', installation_id, 'lockDigest', lock_digest,
'generation', generation, 'generationDigest', generation_digest,
'materializedRevisionDigest', materialized_revision_digest,
'publicationDigest', publication_digest, 'promptId', prompt_id,
'promptDefinitionDigest', prompt_definition_digest
) AND
jsonb_typeof(receipt_json) = 'object' AND
octet_length(receipt_json::text) BETWEEN 2 AND 16384 AND
receipt_json @> jsonb_build_object(
'schema', 'qinglong/plugin-package-prompt-admission-receipt@v1',
'requestId', request_id, 'invocationId', invocation_id,
'planDigest', plan_digest, 'runId', run_id, 'stepRunId', step_run_id,
'publicationDigest', publication_digest, 'promptId', prompt_id,
'admittedAtMs', admitted_at_ms, 'receiptDigest', receipt_digest
)
)
)`;
const postgresPluginPackagePromptAdmissionMigration =
defineSqlMigration<PostgresQueryable>(
POSTGRES_PLUGIN_PACKAGE_PROMPT_ADMISSION_MIGRATION_ID,
[
POSTGRES_PLUGIN_PACKAGE_PROMPT_ADMISSION_TABLE_SQL,
`CREATE UNIQUE INDEX ql3_ai_prompt_admission_run_step_uidx
ON "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_prompt_admissions"
(run_id, step_run_id)`,
`CREATE INDEX ql3_ai_prompt_admission_target_idx
ON "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_prompt_admissions"
(project_id, package_name, admitted_at_ms, request_id)`,
`REVOKE ALL ON TABLE
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_prompt_admissions"
FROM PUBLIC`,
`GRANT SELECT, INSERT ON TABLE
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_prompt_admissions"
TO ql3_runtime`,
`CREATE FUNCTION
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."plugin_package_prompt_admission_snapshot"(
p_project_id varchar,
p_package_name varchar,
p_publication_digest char(64),
p_requested_by_subject_type varchar,
p_requested_by_subject_id varchar,
p_project_version integer,
p_binding_version integer
)
RETURNS TABLE(publication_json jsonb, revision_json jsonb)
LANGUAGE plpgsql
VOLATILE
SECURITY DEFINER
SET search_path = pg_catalog, ql3, ql3_ai
AS $ql3_ai$
BEGIN
IF NOT pg_has_role(session_user, 'ql3_runtime', 'member') THEN
RAISE EXCEPTION 'Runtime authority is required'
USING ERRCODE = 'insufficient_privilege';
END IF;
PERFORM 1
FROM "ql3"."projects" AS project
JOIN "ql3"."project_role_bindings" AS binding
ON binding.project_id = project.id
AND binding.subject_type = p_requested_by_subject_type
AND binding.subject_id = p_requested_by_subject_id
AND binding.version = p_binding_version
WHERE project.id = p_project_id
AND project.status = 'active'
AND project.version = p_project_version
AND binding.state = 'active'
AND binding.version = (
SELECT max(candidate.version)
FROM "ql3"."project_role_bindings" AS candidate
WHERE candidate.project_id = p_project_id
AND candidate.subject_type = p_requested_by_subject_type
AND candidate.subject_id = p_requested_by_subject_id
)
FOR SHARE OF project, binding;
IF NOT FOUND THEN
RETURN;
END IF;
IF NOT "ql3"."plugin_package_automation_start_allowed"(
p_project_id, p_package_name, p_publication_digest
) THEN
RETURN;
END IF;
RETURN QUERY
SELECT publication.publication_json, revision.revision_json
FROM "ql3"."plugin_package_automation_publications" AS publication
JOIN "ql3"."plugin_package_materialized_revisions" AS revision
ON revision.generation_digest = publication.generation_digest
AND revision.revision_digest =
publication.materialized_revision_digest
WHERE publication.project_id = p_project_id
AND publication.package_name = p_package_name
AND publication.publication_digest = p_publication_digest
AND publication.state = 'active'
FOR SHARE OF publication, revision;
END
$ql3_ai$`,
`REVOKE ALL ON FUNCTION
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."plugin_package_prompt_admission_snapshot"(
varchar, varchar, char(64), varchar, varchar, integer, integer
)
FROM PUBLIC, ql3_runtime, ql3_admin, ql3_package_manager,
ql3_package_executor, ql3_worker_ingress`,
`GRANT EXECUTE ON FUNCTION
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."plugin_package_prompt_admission_snapshot"(
varchar, varchar, char(64), varchar, varchar, integer, integer
)
TO ql3_runtime`,
],
(context, statement) => context.query(statement).then(() => undefined),
);
const POSTGRES_PLUGIN_PACKAGE_PROMPT_FINALIZATION_TABLE_SQL = `
CREATE TABLE "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_prompt_finalizations" (
request_id varchar(128) PRIMARY KEY,
invocation_id varchar(128) NOT NULL UNIQUE,
plan_digest char(64) NOT NULL UNIQUE,
run_id varchar(36) NOT NULL UNIQUE,
step_run_id varchar(128) NOT NULL UNIQUE,
terminal_evidence_kind varchar(16) NOT NULL,
terminal_evidence_digest char(64) NOT NULL UNIQUE,
final_step_run_digest char(64) NOT NULL,
run_status varchar(16) NOT NULL,
event_id varchar(36) NOT NULL UNIQUE,
final_run_version integer NOT NULL,
final_run_event_sequence integer NOT NULL,
finalized_at_ms bigint NOT NULL,
receipt_digest char(64) NOT NULL UNIQUE,
receipt_json jsonb NOT NULL,
CONSTRAINT ql3_ai_prompt_finalization_admission_fk
FOREIGN KEY (request_id)
REFERENCES "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_prompt_admissions"
(request_id) ON DELETE RESTRICT,
CONSTRAINT ql3_ai_prompt_finalization_invocation_fk
FOREIGN KEY (invocation_id)
REFERENCES "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_prompt_admissions"
(invocation_id) ON DELETE RESTRICT,
CONSTRAINT ql3_ai_prompt_finalization_plan_fk
FOREIGN KEY (plan_digest)
REFERENCES "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_prompt_admissions"
(plan_digest) ON DELETE RESTRICT,
CONSTRAINT ql3_ai_prompt_finalization_run_fk
FOREIGN KEY (run_id) REFERENCES "ql3"."runs" (id) ON DELETE RESTRICT,
CONSTRAINT ql3_ai_prompt_finalization_step_fk
FOREIGN KEY (run_id, step_run_id)
REFERENCES "ql3"."step_runs" (run_id, id) ON DELETE RESTRICT,
CONSTRAINT ql3_ai_prompt_finalization_event_fk
FOREIGN KEY (event_id) REFERENCES "ql3"."run_events" (id)
ON DELETE RESTRICT,
CONSTRAINT ql3_ai_prompt_finalization_identity_check CHECK (
request_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
invocation_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
run_id ~ '^[A-Za-z0-9][A-Za-z0-9._:-]{0,35}$' AND
step_run_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
event_id ~ '^[A-Za-z0-9][A-Za-z0-9._:-]{0,35}$' AND
terminal_evidence_kind IN ('completion', 'resolution') AND
run_status IN ('succeeded', 'failed', 'cancelled', 'timed_out') AND
final_run_version BETWEEN 3 AND 2147483647 AND
final_run_event_sequence = final_run_version AND finalized_at_ms >= 0
),
CONSTRAINT ql3_ai_prompt_finalization_digest_check CHECK (
plan_digest ~ '^[0-9a-f]{64}$' AND
terminal_evidence_digest ~ '^[0-9a-f]{64}$' AND
final_step_run_digest ~ '^[0-9a-f]{64}$' AND
receipt_digest ~ '^[0-9a-f]{64}$'
),
CONSTRAINT ql3_ai_prompt_finalization_json_check CHECK (
jsonb_typeof(receipt_json) = 'object' AND
octet_length(receipt_json::text) BETWEEN 2 AND 16384 AND
receipt_json @> jsonb_build_object(
'schema', 'qinglong/plugin-package-prompt-finalization-receipt@v1',
'requestId', request_id, 'invocationId', invocation_id,
'planDigest', plan_digest, 'runId', run_id, 'stepRunId', step_run_id,
'terminalEvidenceKind', terminal_evidence_kind,
'terminalEvidenceDigest', terminal_evidence_digest,
'finalStepRunDigest', final_step_run_digest, 'runStatus', run_status,
'eventId', event_id, 'finalRunVersion', final_run_version,
'finalRunEventSequence', final_run_event_sequence,
'finalizedAtMs', finalized_at_ms, 'receiptDigest', receipt_digest
)
)
)`;
const postgresPluginPackagePromptFinalizationMigration =
defineSqlMigration<PostgresQueryable>(
POSTGRES_PLUGIN_PACKAGE_PROMPT_FINALIZATION_MIGRATION_ID,
[
POSTGRES_PLUGIN_PACKAGE_PROMPT_FINALIZATION_TABLE_SQL,
`CREATE UNIQUE INDEX ql3_ai_prompt_finalization_run_step_uidx
ON "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_prompt_finalizations"
(run_id, step_run_id)`,
`CREATE INDEX ql3_ai_prompt_finalization_status_idx
ON "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_prompt_finalizations"
(run_status, finalized_at_ms, request_id)`,
`REVOKE ALL ON TABLE
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_prompt_finalizations"
FROM PUBLIC`,
`GRANT SELECT, INSERT ON TABLE
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_prompt_finalizations"
TO ql3_runtime`,
`GRANT SELECT ON TABLE
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."${POSTGRES_MODEL_INVOCATION_MIGRATION_HISTORY_TABLE}"
TO ql3_runtime`,
],
(context, statement) => context.query(statement).then(() => undefined),
);
const POSTGRES_PLUGIN_PACKAGE_PROMPT_OUTPUT_ARTIFACT_TABLE_SQL = `
CREATE TABLE "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_prompt_output_artifacts" (
artifact_id varchar(128) PRIMARY KEY,
project_id varchar(128) NOT NULL,
run_id varchar(36) NOT NULL,
step_run_id varchar(128) NOT NULL,
invocation_id varchar(128) NOT NULL UNIQUE,
requested_by_type varchar(32) NOT NULL,
requested_by_id varchar(128) NOT NULL,
provider varchar(256) NOT NULL,
model varchar(256) NOT NULL,
content_digest char(64) NOT NULL,
output_bytes integer NOT NULL,
retention_policy_revision varchar(128) NOT NULL,
retention_ms bigint NOT NULL,
retention_policy_digest char(64) NOT NULL,
retention_eligible_at_ms bigint NOT NULL,
key_id varchar(128) NOT NULL,
algorithm varchar(16) NOT NULL,
plaintext_bytes integer NOT NULL,
sealed_at_ms bigint NOT NULL,
artifact_digest char(64) NOT NULL UNIQUE,
artifact_json jsonb NOT NULL,
CONSTRAINT ql3_ai_prompt_output_artifact_admission_fk
FOREIGN KEY (invocation_id)
REFERENCES "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_prompt_admissions"
(invocation_id) ON DELETE RESTRICT,
CONSTRAINT ql3_ai_prompt_output_artifact_start_fk
FOREIGN KEY (invocation_id)
REFERENCES "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_starts"
(invocation_id) ON DELETE RESTRICT,
CONSTRAINT ql3_ai_prompt_output_artifact_step_fk
FOREIGN KEY (run_id, step_run_id)
REFERENCES "ql3"."step_runs" (run_id, id) ON DELETE RESTRICT,
CONSTRAINT ql3_ai_prompt_output_artifact_identity_check CHECK (
artifact_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
project_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
run_id ~ '^[A-Za-z0-9][A-Za-z0-9._:-]{0,35}$' AND
step_run_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
invocation_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
length(requested_by_type) BETWEEN 1 AND 32 AND
requested_by_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
length(provider) BETWEEN 1 AND 256 AND
length(model) BETWEEN 1 AND 256 AND
retention_policy_revision ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
key_id ~ '^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$' AND
algorithm = 'aes-256-gcm'
),
CONSTRAINT ql3_ai_prompt_output_artifact_value_check CHECK (
output_bytes BETWEEN 0 AND 1048576 AND
plaintext_bytes BETWEEN 1 AND 1052672 AND
retention_ms BETWEEN 3600000 AND 31536000000 AND
sealed_at_ms >= 0 AND
retention_eligible_at_ms = sealed_at_ms + retention_ms AND
content_digest ~ '^[0-9a-f]{64}$' AND
retention_policy_digest ~ '^[0-9a-f]{64}$' AND
artifact_digest ~ '^[0-9a-f]{64}$'
),
CONSTRAINT ql3_ai_prompt_output_artifact_json_check CHECK (
jsonb_typeof(artifact_json) = 'object' AND
octet_length(artifact_json::text) BETWEEN 2 AND 1572864 AND
artifact_json @> jsonb_build_object(
'schema', 'qinglong/plugin-package-prompt-output-artifact@v1',
'artifactId', artifact_id, 'projectId', project_id,
'runId', run_id, 'stepRunId', step_run_id,
'invocationId', invocation_id, 'provider', provider, 'model', model,
'contentDigest', content_digest, 'outputBytes', output_bytes,
'retentionPolicyDigest', retention_policy_digest,
'retentionEligibleAtMs', retention_eligible_at_ms,
'keyId', key_id, 'algorithm', algorithm,
'plaintextBytes', plaintext_bytes, 'sealedAtMs', sealed_at_ms,
'artifactDigest', artifact_digest
) AND
artifact_json -> 'requestedBy' @> jsonb_build_object(
'type', requested_by_type, 'id', requested_by_id
) AND
artifact_json -> 'retentionPolicy' @> jsonb_build_object(
'revision', retention_policy_revision, 'retentionMs', retention_ms
)
)
)`;
const postgresPluginPackagePromptOutputArtifactMigration =
defineSqlMigration<PostgresQueryable>(
POSTGRES_PLUGIN_PACKAGE_PROMPT_OUTPUT_ARTIFACT_MIGRATION_ID,
[
POSTGRES_PLUGIN_PACKAGE_PROMPT_OUTPUT_ARTIFACT_TABLE_SQL,
`CREATE INDEX ql3_ai_prompt_output_artifact_retention_idx
ON "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_prompt_output_artifacts"
(retention_eligible_at_ms, artifact_id)`,
`CREATE INDEX ql3_ai_prompt_output_artifact_project_run_idx
ON "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_prompt_output_artifacts"
(project_id, run_id, artifact_id)`,
`REVOKE ALL ON TABLE
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_prompt_output_artifacts"
FROM PUBLIC`,
`GRANT SELECT, INSERT ON TABLE
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_prompt_output_artifacts"
TO ql3_runtime`,
],
(context, statement) => context.query(statement).then(() => undefined),
);
const POSTGRES_PLUGIN_PACKAGE_PROMPT_OUTPUT_TOMBSTONE_TABLE_SQL = `
CREATE TABLE "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_prompt_output_artifact_tombstones" (
artifact_id varchar(128) PRIMARY KEY,
project_id varchar(128) NOT NULL,
run_id varchar(36) NOT NULL,
step_run_id varchar(128) NOT NULL,
invocation_id varchar(128) NOT NULL UNIQUE,
artifact_digest char(64) NOT NULL UNIQUE,
retention_policy_digest char(64) NOT NULL,
retention_eligible_at_ms bigint NOT NULL,
key_id varchar(128) NOT NULL,
tombstoned_at_ms bigint NOT NULL,
tombstone_digest char(64) NOT NULL UNIQUE,
tombstone_json jsonb NOT NULL,
CONSTRAINT ql3_ai_prompt_output_tombstone_admission_fk
FOREIGN KEY (invocation_id)
REFERENCES "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_prompt_admissions"
(invocation_id) ON DELETE RESTRICT,
CONSTRAINT ql3_ai_prompt_output_tombstone_start_fk
FOREIGN KEY (invocation_id)
REFERENCES "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_starts"
(invocation_id) ON DELETE RESTRICT,
CONSTRAINT ql3_ai_prompt_output_tombstone_step_fk
FOREIGN KEY (run_id, step_run_id)
REFERENCES "ql3"."step_runs" (run_id, id) ON DELETE RESTRICT,
CONSTRAINT ql3_ai_prompt_output_tombstone_identity_check CHECK (
artifact_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
project_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
run_id ~ '^[A-Za-z0-9][A-Za-z0-9._:-]{0,35}$' AND
step_run_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
invocation_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
key_id ~ '^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$'
),
CONSTRAINT ql3_ai_prompt_output_tombstone_value_check CHECK (
retention_eligible_at_ms >= 0 AND
tombstoned_at_ms >= retention_eligible_at_ms AND
artifact_digest ~ '^[0-9a-f]{64}$' AND
retention_policy_digest ~ '^[0-9a-f]{64}$' AND
tombstone_digest ~ '^[0-9a-f]{64}$'
),
CONSTRAINT ql3_ai_prompt_output_tombstone_json_check CHECK (
jsonb_typeof(tombstone_json) = 'object' AND
octet_length(tombstone_json::text) BETWEEN 2 AND 8192 AND
tombstone_json @> jsonb_build_object(
'schema', 'qinglong/plugin-package-prompt-output-artifact-tombstone@v1',
'tombstonedAtMs', tombstoned_at_ms,
'tombstoneDigest', tombstone_digest
) AND
tombstone_json -> 'reference' @> jsonb_build_object(
'artifactId', artifact_id, 'projectId', project_id,
'runId', run_id, 'stepRunId', step_run_id,
'invocationId', invocation_id, 'artifactDigest', artifact_digest,
'retentionPolicyDigest', retention_policy_digest,
'retentionEligibleAtMs', retention_eligible_at_ms, 'keyId', key_id
)
)
)`;
const postgresPluginPackagePromptOutputTombstoneMigration =
defineSqlMigration<PostgresQueryable>(
POSTGRES_PLUGIN_PACKAGE_PROMPT_OUTPUT_TOMBSTONE_MIGRATION_ID,
[
POSTGRES_PLUGIN_PACKAGE_PROMPT_OUTPUT_TOMBSTONE_TABLE_SQL,
`CREATE INDEX ql3_ai_prompt_output_tombstone_time_idx
ON "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_prompt_output_artifact_tombstones"
(tombstoned_at_ms, artifact_id)`,
`CREATE INDEX ql3_ai_prompt_output_tombstone_project_run_idx
ON "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_prompt_output_artifact_tombstones"
(project_id, run_id, artifact_id)`,
`REVOKE ALL ON TABLE
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_prompt_output_artifact_tombstones"
FROM PUBLIC`,
`DO $$
BEGIN
EXECUTE format(
'GRANT CONNECT ON DATABASE %I TO ql3_ai_maintenance',
current_database()
);
END
$$`,
`GRANT SELECT ON TABLE
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_prompt_output_artifact_tombstones"
TO ql3_runtime`,
`GRANT USAGE ON SCHEMA "${POSTGRES_MODEL_INVOCATION_SCHEMA}"
TO ql3_ai_maintenance`,
`GRANT SELECT ON TABLE
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."${POSTGRES_MODEL_INVOCATION_MIGRATION_HISTORY_TABLE}",
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_completions",
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_prompt_admissions",
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_prompt_finalizations"
TO ql3_ai_maintenance`,
`GRANT SELECT, DELETE ON TABLE
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_prompt_output_artifacts"
TO ql3_ai_maintenance`,
`GRANT SELECT, INSERT ON TABLE
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_prompt_output_artifact_tombstones"
TO ql3_ai_maintenance`,
`GRANT USAGE ON SCHEMA "ql3" TO ql3_ai_maintenance`,
`GRANT SELECT ON TABLE "ql3"."runs", "ql3"."step_runs"
TO ql3_ai_maintenance`,
],
(context, statement) => context.query(statement).then(() => undefined),
);
const POSTGRES_PLUGIN_PACKAGE_PROMPT_OUTPUT_KEY_RETIREMENT_PREPARATION_TABLE_SQL = `
CREATE TABLE "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_prompt_output_key_retirement_preparations" (
key_id varchar(128) PRIMARY KEY,
retirement_id varchar(128) NOT NULL UNIQUE,
request_id varchar(128) NOT NULL UNIQUE,
mutation_id varchar(128) NOT NULL UNIQUE,
catalog_digest char(64) NOT NULL,
material_proof char(64) NOT NULL,
prepared_at_ms bigint NOT NULL,
preparation_digest char(64) NOT NULL UNIQUE,
preparation_json jsonb NOT NULL,
CONSTRAINT ql3_ai_prompt_output_key_retirement_preparation_fk_uidx
UNIQUE (key_id, preparation_digest),
CONSTRAINT ql3_ai_prompt_output_key_retirement_preparation_identity_check CHECK (
key_id ~ '^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$' AND
retirement_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
request_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
mutation_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$'
),
CONSTRAINT ql3_ai_prompt_output_key_retirement_preparation_value_check CHECK (
prepared_at_ms >= 0 AND
catalog_digest ~ '^[0-9a-f]{64}$' AND
material_proof ~ '^[0-9a-f]{64}$' AND
preparation_digest ~ '^[0-9a-f]{64}$'
),
CONSTRAINT ql3_ai_prompt_output_key_retirement_preparation_json_check CHECK (
jsonb_typeof(preparation_json) = 'object' AND
octet_length(preparation_json::text) BETWEEN 2 AND 8192 AND
preparation_json @> jsonb_build_object(
'schema', 'qinglong/plugin-package-prompt-output-key-retirement-preparation@v1',
'keyId', key_id, 'retirementId', retirement_id,
'requestId', request_id, 'mutationId', mutation_id,
'catalogDigest', catalog_digest, 'materialProof', material_proof,
'preparedAtMs', prepared_at_ms,
'preparationDigest', preparation_digest
)
)
)`;
const POSTGRES_PLUGIN_PACKAGE_PROMPT_OUTPUT_KEY_RETIREMENT_COMPLETION_TABLE_SQL = `
CREATE TABLE "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_prompt_output_key_retirement_completions" (
key_id varchar(128) PRIMARY KEY,
retirement_id varchar(128) NOT NULL UNIQUE,
request_id varchar(128) NOT NULL UNIQUE,
mutation_id varchar(128) NOT NULL UNIQUE,
preparation_digest char(64) NOT NULL UNIQUE,
retired_catalog_digest char(64) NOT NULL,
absence_proof char(64) NOT NULL,
completed_at_ms bigint NOT NULL,
completion_digest char(64) NOT NULL UNIQUE,
completion_json jsonb NOT NULL,
CONSTRAINT ql3_ai_prompt_output_key_retirement_completion_preparation_fk
FOREIGN KEY (key_id, preparation_digest)
REFERENCES "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_prompt_output_key_retirement_preparations"
(key_id, preparation_digest) ON DELETE RESTRICT,
CONSTRAINT ql3_ai_prompt_output_key_retirement_completion_identity_check CHECK (
key_id ~ '^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$' AND
retirement_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
request_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
mutation_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$'
),
CONSTRAINT ql3_ai_prompt_output_key_retirement_completion_value_check CHECK (
completed_at_ms >= 0 AND
preparation_digest ~ '^[0-9a-f]{64}$' AND
retired_catalog_digest ~ '^[0-9a-f]{64}$' AND
absence_proof ~ '^[0-9a-f]{64}$' AND
completion_digest ~ '^[0-9a-f]{64}$'
),
CONSTRAINT ql3_ai_prompt_output_key_retirement_completion_json_check CHECK (
jsonb_typeof(completion_json) = 'object' AND
octet_length(completion_json::text) BETWEEN 2 AND 8192 AND
completion_json @> jsonb_build_object(
'schema', 'qinglong/plugin-package-prompt-output-key-retirement-completion@v1',
'keyId', key_id, 'retirementId', retirement_id,
'requestId', request_id, 'mutationId', mutation_id,
'preparationDigest', preparation_digest,
'retiredCatalogDigest', retired_catalog_digest,
'absenceProof', absence_proof, 'completedAtMs', completed_at_ms,
'completionDigest', completion_digest
)
)
)`;
const postgresPluginPackagePromptOutputKeyRetirementMigration =
defineSqlMigration<PostgresQueryable>(
POSTGRES_PLUGIN_PACKAGE_PROMPT_OUTPUT_KEY_RETIREMENT_MIGRATION_ID,
[
POSTGRES_PLUGIN_PACKAGE_PROMPT_OUTPUT_KEY_RETIREMENT_PREPARATION_TABLE_SQL,
POSTGRES_PLUGIN_PACKAGE_PROMPT_OUTPUT_KEY_RETIREMENT_COMPLETION_TABLE_SQL,
`CREATE INDEX ql3_ai_prompt_output_key_retirement_completion_time_idx
ON "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_prompt_output_key_retirement_completions"
(completed_at_ms, key_id)`,
`REVOKE ALL ON TABLE
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_prompt_output_key_retirement_preparations",
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_prompt_output_key_retirement_completions"
FROM PUBLIC`,
`GRANT SELECT ON TABLE
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_prompt_output_key_retirement_preparations"
TO ql3_runtime`,
`GRANT SELECT, INSERT ON TABLE
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_prompt_output_key_retirement_preparations",
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_prompt_output_key_retirement_completions"
TO ql3_ai_maintenance`,
],
(context, statement) => context.query(statement).then(() => undefined),
);
const POSTGRES_PLUGIN_PACKAGE_PROMPT_OUTPUT_KEY_ROTATION_PREPARATION_TABLE_SQL = `
CREATE TABLE "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_prompt_output_key_rotation_preparations" (
rotation_id varchar(128) PRIMARY KEY,
request_id varchar(128) NOT NULL UNIQUE,
mutation_id varchar(128) NOT NULL UNIQUE,
expected_secret_uid varchar(128) NOT NULL,
expected_active_key_id varchar(128) NOT NULL,
expected_catalog_digest char(64) NOT NULL,
new_key_id varchar(128) NOT NULL UNIQUE,
material_proof char(64) NOT NULL,
prepared_at_ms bigint NOT NULL,
preparation_digest char(64) NOT NULL UNIQUE,
preparation_json jsonb NOT NULL,
CONSTRAINT ql3_ai_prompt_key_rotation_preparation_fk_uidx
UNIQUE (rotation_id, preparation_digest),
CONSTRAINT ql3_ai_prompt_key_rotation_source_uidx
UNIQUE (expected_secret_uid, expected_catalog_digest),
CONSTRAINT ql3_ai_prompt_key_rotation_preparation_identity_check CHECK (
rotation_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
request_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
mutation_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
expected_secret_uid ~ '^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$' AND
expected_active_key_id ~ '^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$' AND
new_key_id ~ '^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$' AND
expected_active_key_id <> new_key_id
),
CONSTRAINT ql3_ai_prompt_key_rotation_preparation_value_check CHECK (
prepared_at_ms >= 0 AND
expected_catalog_digest ~ '^[0-9a-f]{64}$' AND
material_proof ~ '^[0-9a-f]{64}$' AND
preparation_digest ~ '^[0-9a-f]{64}$'
),
CONSTRAINT ql3_ai_prompt_key_rotation_preparation_json_check CHECK (
jsonb_typeof(preparation_json) = 'object' AND
octet_length(preparation_json::text) BETWEEN 2 AND 8192 AND
preparation_json @> jsonb_build_object(
'schema', 'qinglong/plugin-package-prompt-output-key-rotation-preparation@v1',
'rotationId', rotation_id, 'requestId', request_id,
'mutationId', mutation_id, 'expectedSecretUid', expected_secret_uid,
'expectedActiveKeyId', expected_active_key_id,
'expectedCatalogDigest', expected_catalog_digest,
'newKeyId', new_key_id, 'materialProof', material_proof,
'preparedAtMs', prepared_at_ms, 'preparationDigest', preparation_digest
)
)
)`;
const POSTGRES_PLUGIN_PACKAGE_PROMPT_OUTPUT_KEY_ROTATION_COMPLETION_TABLE_SQL = `
CREATE TABLE "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_prompt_output_key_rotation_completions" (
rotation_id varchar(128) PRIMARY KEY,
request_id varchar(128) NOT NULL UNIQUE,
mutation_id varchar(128) NOT NULL UNIQUE,
preparation_digest char(64) NOT NULL UNIQUE,
generation bigint NOT NULL,
previous_active_key_id varchar(128) NOT NULL,
active_key_id varchar(128) NOT NULL,
catalog_digest char(64) NOT NULL,
material_proof char(64) NOT NULL,
completed_at_ms bigint NOT NULL,
completion_digest char(64) NOT NULL UNIQUE,
completion_json jsonb NOT NULL,
CONSTRAINT ql3_ai_prompt_key_rotation_completion_preparation_fk
FOREIGN KEY (rotation_id, preparation_digest)
REFERENCES "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_prompt_output_key_rotation_preparations"
(rotation_id, preparation_digest) ON DELETE RESTRICT,
CONSTRAINT ql3_ai_prompt_key_rotation_completion_identity_check CHECK (
rotation_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
request_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
mutation_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
previous_active_key_id ~ '^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$' AND
active_key_id ~ '^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$' AND
previous_active_key_id <> active_key_id
),
CONSTRAINT ql3_ai_prompt_key_rotation_completion_value_check CHECK (
generation BETWEEN 2 AND 9007199254740991 AND completed_at_ms >= 0 AND
preparation_digest ~ '^[0-9a-f]{64}$' AND
catalog_digest ~ '^[0-9a-f]{64}$' AND
material_proof ~ '^[0-9a-f]{64}$' AND
completion_digest ~ '^[0-9a-f]{64}$'
),
CONSTRAINT ql3_ai_prompt_key_rotation_completion_json_check CHECK (
jsonb_typeof(completion_json) = 'object' AND
octet_length(completion_json::text) BETWEEN 2 AND 8192 AND
completion_json @> jsonb_build_object(
'schema', 'qinglong/plugin-package-prompt-output-key-rotation-completion@v1',
'rotationId', rotation_id, 'requestId', request_id,
'mutationId', mutation_id, 'preparationDigest', preparation_digest,
'generation', generation, 'previousActiveKeyId', previous_active_key_id,
'activeKeyId', active_key_id, 'catalogDigest', catalog_digest,
'materialProof', material_proof, 'completedAtMs', completed_at_ms,
'completionDigest', completion_digest
)
)
)`;
const postgresPluginPackagePromptOutputKeyRotationMigration =
defineSqlMigration<PostgresQueryable>(
POSTGRES_PLUGIN_PACKAGE_PROMPT_OUTPUT_KEY_ROTATION_MIGRATION_ID,
[
POSTGRES_PLUGIN_PACKAGE_PROMPT_OUTPUT_KEY_ROTATION_PREPARATION_TABLE_SQL,
POSTGRES_PLUGIN_PACKAGE_PROMPT_OUTPUT_KEY_ROTATION_COMPLETION_TABLE_SQL,
`CREATE INDEX ql3_ai_prompt_key_rotation_completion_time_idx
ON "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_prompt_output_key_rotation_completions"
(completed_at_ms, rotation_id)`,
`REVOKE ALL ON TABLE
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_prompt_output_key_rotation_preparations",
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_prompt_output_key_rotation_completions"
FROM PUBLIC, ql3_runtime, ql3_admin, ql3_package_manager,
ql3_package_executor, ql3_automation_manager, ql3_worker_ingress,
ql3_worker_credential_manager, ql3_worker_credential_executor,
ql3_ai_credential_manager, ql3_ai_credential_tester`,
`GRANT SELECT, INSERT ON TABLE
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_prompt_output_key_rotation_preparations",
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_prompt_output_key_rotation_completions"
TO ql3_ai_maintenance`,
],
(context, statement) => context.query(statement).then(() => undefined),
);
const postgresPluginPackagePromptProductAuthorizationMigration =
defineSqlMigration<PostgresQueryable>(
POSTGRES_PLUGIN_PACKAGE_PROMPT_PRODUCT_AUTHORIZATION_MIGRATION_ID,
[
`CREATE FUNCTION
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."plugin_package_prompt_authorize_admission"(
p_credential_id varchar,
p_credential_version integer,
p_project_id varchar,
p_subject_type varchar,
p_subject_id varchar,
p_project_version integer,
p_binding_version integer,
p_audit_event_id uuid,
p_request_id varchar,
p_planned_at_ms bigint,
p_replay boolean
)
RETURNS boolean
LANGUAGE plpgsql
VOLATILE
SECURITY DEFINER
SET search_path = pg_catalog, ql3, ql3_ai
AS $ql3_ai$
BEGIN
IF NOT pg_has_role(session_user, 'ql3_runtime', 'member') THEN
RAISE EXCEPTION 'Runtime authority is required'
USING ERRCODE = 'insufficient_privilege';
END IF;
PERFORM pg_advisory_xact_lock(
hashtextextended('ql3-api-credential:' || p_credential_id, 0)
);
PERFORM pg_advisory_xact_lock(
hashtextextended(
'ql3-identity:' || p_subject_type || ':' || p_subject_id,
0
)
);
PERFORM 1
FROM "ql3"."api_credentials" AS credential
JOIN "ql3"."identity_subjects" AS identity
ON identity.subject_type = credential.subject_type
AND identity.subject_id = credential.subject_id
WHERE credential.credential_id = p_credential_id
AND credential.version = p_credential_version
AND credential.version = (
SELECT max(candidate.version)
FROM "ql3"."api_credentials" AS candidate
WHERE candidate.credential_id = p_credential_id
)
AND credential.state = 'active'
AND credential.subject_type = p_subject_type
AND credential.subject_id = p_subject_id
AND credential.not_before_at_ms <=
floor(extract(epoch FROM clock_timestamp()) * 1000)::bigint
AND credential.expires_at_ms >
floor(extract(epoch FROM clock_timestamp()) * 1000)::bigint
AND identity.status = 'active'
FOR SHARE OF credential, identity;
IF NOT FOUND THEN
RETURN false;
END IF;
PERFORM 1
FROM "ql3"."projects" AS project
JOIN "ql3"."project_role_bindings" AS binding
ON binding.project_id = project.id
AND binding.subject_type = p_subject_type
AND binding.subject_id = p_subject_id
AND binding.version = p_binding_version
WHERE project.id = p_project_id
AND project.status = 'active'
AND project.version = p_project_version
AND binding.state = 'active'
AND binding.role IN ('owner', 'admin', 'operator')
AND p_subject_type <> 'agent'
AND binding.version = (
SELECT max(candidate.version)
FROM "ql3"."project_role_bindings" AS candidate
WHERE candidate.project_id = p_project_id
AND candidate.subject_type = p_subject_type
AND candidate.subject_id = p_subject_id
)
FOR SHARE OF project, binding;
IF NOT FOUND THEN
RETURN false;
END IF;
IF NOT p_replay THEN
INSERT INTO "ql3"."security_audit_events" (
event_id, request_id, operation_id, project_id,
subject_type, subject_id, authentication_id, outcome, reasons,
project_version, binding_version, occurred_at_ms
) VALUES (
p_audit_event_id, p_request_id, 'prompt.execute', p_project_id,
p_subject_type, p_subject_id,
'api_credential:' || p_credential_id || ':' || p_credential_version,
'allowed', '["project_policy_allowed"]'::jsonb,
p_project_version, p_binding_version, p_planned_at_ms
);
END IF;
RETURN true;
END
$ql3_ai$`,
`REVOKE ALL ON FUNCTION
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."plugin_package_prompt_authorize_admission"(
varchar, integer, varchar, varchar, varchar, integer, integer,
uuid, varchar, bigint, boolean
)
FROM PUBLIC, ql3_runtime, ql3_admin, ql3_package_manager,
ql3_package_executor, ql3_worker_ingress`,
`GRANT EXECUTE ON FUNCTION
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."plugin_package_prompt_authorize_admission"(
varchar, integer, varchar, varchar, varchar, integer, integer,
uuid, varchar, bigint, boolean
)
TO ql3_runtime`,
],
(context, statement) => context.query(statement).then(() => undefined),
);
export const postgresPromptBaseMigrations = Object.freeze([
postgresPluginPackagePromptAdmissionMigration,
postgresPluginPackagePromptFinalizationMigration,
postgresPluginPackagePromptOutputArtifactMigration,
postgresPluginPackagePromptOutputTombstoneMigration,
postgresPluginPackagePromptOutputKeyRetirementMigration,
]);
export const postgresPromptExtensionMigrations = Object.freeze([
postgresPluginPackagePromptOutputKeyRotationMigration,
postgresPluginPackagePromptProductAuthorizationMigration,
]);
@@ -0,0 +1,377 @@
import type { PostgresQueryable } from '@qinglong/runtime-core';
import {
POSTGRES_MODEL_INVOCATION_USAGE_MIGRATION_ID,
POSTGRES_MODEL_INVOCATION_QUOTA_MIGRATION_ID,
POSTGRES_MODEL_INVOCATION_PRICING_MIGRATION_ID,
POSTGRES_MODEL_INVOCATION_SCHEMA,
} from '../identities';
import { defineSqlMigration } from '../shared';
const POSTGRES_USAGE_LEDGER_TABLE_SQL = `
CREATE TABLE "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_usage_ledger" (
invocation_id varchar(128) PRIMARY KEY,
project_id varchar(128) NOT NULL,
run_id varchar(36) NOT NULL,
step_run_id varchar(128) NOT NULL,
trace_id varchar(128) NOT NULL,
provider varchar(128) NOT NULL,
model varchar(128) NOT NULL,
policy_revision varchar(128) NOT NULL,
completion_digest char(64) NOT NULL,
outcome varchar(32) NOT NULL,
settled_at_ms bigint NOT NULL,
input_bytes integer NOT NULL,
output_bytes integer NOT NULL,
input_tokens bigint NOT NULL,
output_tokens bigint NOT NULL,
total_tokens bigint NOT NULL,
cost_micros bigint,
ledger_digest char(64) NOT NULL,
record_json jsonb NOT NULL,
FOREIGN KEY (invocation_id, completion_digest)
REFERENCES "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_completions"
(invocation_id, completion_digest) ON DELETE RESTRICT,
CONSTRAINT ql3_model_invocation_usage_identity_check CHECK (
invocation_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
project_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
run_id ~ '^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$' AND
step_run_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
trace_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
provider ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
model ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
policy_revision ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$'
),
CONSTRAINT ql3_model_invocation_usage_value_check CHECK (
outcome IN ('succeeded', 'failed', 'timed_out', 'outcome_unknown') AND
settled_at_ms >= 0 AND
input_bytes BETWEEN 1 AND 262144 AND
output_bytes BETWEEN 0 AND 1048576 AND
input_tokens >= 0 AND output_tokens >= 0 AND
total_tokens = input_tokens + output_tokens AND
(cost_micros IS NULL OR cost_micros >= 0) AND
completion_digest ~ '^[0-9a-f]{64}$' AND
ledger_digest ~ '^[0-9a-f]{64}$'
),
CONSTRAINT ql3_model_invocation_usage_json_check CHECK (
jsonb_typeof(record_json) = 'object' AND
octet_length(record_json::text) BETWEEN 2 AND 24576 AND
record_json @> jsonb_build_object(
'schema', 'qinglong/model-invocation-usage-ledger@v1',
'invocationId', invocation_id,
'projectId', project_id,
'runId', run_id,
'stepRunId', step_run_id,
'traceId', trace_id,
'provider', provider,
'model', model,
'policyRevision', policy_revision,
'completionDigest', completion_digest,
'outcome', outcome,
'settledAtMs', settled_at_ms,
'inputBytes', input_bytes,
'outputBytes', output_bytes,
'inputTokens', input_tokens,
'outputTokens', output_tokens,
'totalTokens', total_tokens,
'costMicros', cost_micros,
'ledgerDigest', ledger_digest
)
)
)`;
const postgresUsageMigration = defineSqlMigration<PostgresQueryable>(
POSTGRES_MODEL_INVOCATION_USAGE_MIGRATION_ID,
[
`CREATE UNIQUE INDEX ql3_model_invocation_completions_identity_digest_uidx
ON "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_completions"
(invocation_id, completion_digest)`,
POSTGRES_USAGE_LEDGER_TABLE_SQL,
`CREATE INDEX ql3_model_invocation_usage_project_time_idx
ON "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_usage_ledger"
(project_id, settled_at_ms, invocation_id)`,
`CREATE UNIQUE INDEX ql3_model_invocation_usage_digest_uidx
ON "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_usage_ledger"
(ledger_digest)`,
`REVOKE ALL ON TABLE
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_usage_ledger"
FROM PUBLIC`,
`GRANT SELECT, INSERT ON TABLE
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_usage_ledger"
TO ql3_runtime`,
],
(context, statement) => context.query(statement).then(() => undefined),
);
const POSTGRES_QUOTA_RESERVATION_TABLE_SQL = `
CREATE TABLE "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_quota_reservations" (
invocation_id varchar(128) PRIMARY KEY,
project_id varchar(128) NOT NULL,
model_policy_revision varchar(128) NOT NULL,
quota_policy_revision varchar(128) NOT NULL,
window_ms bigint NOT NULL,
window_start_ms bigint NOT NULL,
window_end_ms bigint NOT NULL,
max_invocations integer NOT NULL,
max_tokens bigint NOT NULL,
max_cost_micros bigint,
reserved_tokens bigint NOT NULL,
reserved_cost_micros bigint,
reserved_at_ms bigint NOT NULL,
admission_digest char(64) NOT NULL,
reservation_digest char(64) NOT NULL,
record_json jsonb NOT NULL,
FOREIGN KEY (invocation_id)
REFERENCES "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_starts"
(invocation_id) ON DELETE RESTRICT,
CONSTRAINT ql3_model_invocation_quota_reservation_value_check CHECK (
window_ms IN (60000, 3600000, 86400000) AND
window_start_ms >= 0 AND window_end_ms = window_start_ms + window_ms AND
reserved_at_ms >= window_start_ms AND reserved_at_ms < window_end_ms AND
max_invocations BETWEEN 1 AND 100000 AND
max_tokens BETWEEN 1 AND 1000000000000 AND
(max_cost_micros IS NULL OR
max_cost_micros BETWEEN 0 AND 1000000000000000) AND
reserved_tokens BETWEEN 1 AND 1000000000000 AND
(reserved_cost_micros IS NULL OR
reserved_cost_micros BETWEEN 0 AND 1000000000000000) AND
(max_cost_micros IS NULL OR reserved_cost_micros IS NOT NULL) AND
admission_digest ~ '^[0-9a-f]{64}$' AND
reservation_digest ~ '^[0-9a-f]{64}$'
),
CONSTRAINT ql3_model_invocation_quota_reservation_json_check CHECK (
jsonb_typeof(record_json) = 'object' AND
octet_length(record_json::text) BETWEEN 2 AND 24576 AND
record_json @> jsonb_build_object(
'schema', 'qinglong/model-invocation-quota-reservation@v1',
'invocationId', invocation_id,
'projectId', project_id,
'modelPolicyRevision', model_policy_revision,
'quotaPolicyRevision', quota_policy_revision,
'windowMs', window_ms,
'windowStartMs', window_start_ms,
'windowEndMs', window_end_ms,
'maxInvocations', max_invocations,
'maxTokens', max_tokens,
'maxCostMicros', max_cost_micros,
'reservedTokens', reserved_tokens,
'reservedCostMicros', reserved_cost_micros,
'reservedAtMs', reserved_at_ms,
'admissionDigest', admission_digest,
'reservationDigest', reservation_digest
)
)
)`;
const POSTGRES_QUOTA_SETTLEMENT_TABLE_SQL = `
CREATE TABLE "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_quota_settlements" (
invocation_id varchar(128) PRIMARY KEY,
project_id varchar(128) NOT NULL,
reservation_digest char(64) NOT NULL,
completion_digest char(64) NOT NULL,
effective_tokens bigint NOT NULL,
effective_cost_micros bigint,
retained_token_reservation boolean NOT NULL,
retained_cost_reservation boolean NOT NULL,
settled_at_ms bigint NOT NULL,
settlement_digest char(64) NOT NULL,
record_json jsonb NOT NULL,
FOREIGN KEY (invocation_id)
REFERENCES "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_quota_reservations"
(invocation_id) ON DELETE RESTRICT,
FOREIGN KEY (invocation_id, completion_digest)
REFERENCES "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_completions"
(invocation_id, completion_digest) ON DELETE RESTRICT,
CONSTRAINT ql3_model_invocation_quota_settlement_value_check CHECK (
effective_tokens BETWEEN 0 AND 1000000000000 AND
(effective_cost_micros IS NULL OR
effective_cost_micros BETWEEN 0 AND 1000000000000000) AND
settled_at_ms >= 0 AND
reservation_digest ~ '^[0-9a-f]{64}$' AND
completion_digest ~ '^[0-9a-f]{64}$' AND
settlement_digest ~ '^[0-9a-f]{64}$'
),
CONSTRAINT ql3_model_invocation_quota_settlement_json_check CHECK (
jsonb_typeof(record_json) = 'object' AND
octet_length(record_json::text) BETWEEN 2 AND 24576 AND
record_json @> jsonb_build_object(
'schema', 'qinglong/model-invocation-quota-settlement@v1',
'invocationId', invocation_id,
'projectId', project_id,
'reservationDigest', reservation_digest,
'completionDigest', completion_digest,
'effectiveTokens', effective_tokens,
'effectiveCostMicros', effective_cost_micros,
'retainedTokenReservation', retained_token_reservation,
'retainedCostReservation', retained_cost_reservation,
'settledAtMs', settled_at_ms,
'settlementDigest', settlement_digest
)
)
)`;
const postgresQuotaMigration = defineSqlMigration<PostgresQueryable>(
POSTGRES_MODEL_INVOCATION_QUOTA_MIGRATION_ID,
[
POSTGRES_QUOTA_RESERVATION_TABLE_SQL,
`CREATE INDEX ql3_model_invocation_quota_reservation_window_idx
ON "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_quota_reservations"
(project_id, window_start_ms, invocation_id)`,
`CREATE UNIQUE INDEX ql3_model_invocation_quota_reservation_digest_uidx
ON "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_quota_reservations"
(reservation_digest)`,
POSTGRES_QUOTA_SETTLEMENT_TABLE_SQL,
`CREATE UNIQUE INDEX ql3_model_invocation_quota_settlement_digest_uidx
ON "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_quota_settlements"
(settlement_digest)`,
`REVOKE ALL ON TABLE
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_quota_reservations",
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_quota_settlements"
FROM PUBLIC`,
`GRANT SELECT, INSERT ON TABLE
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_quota_reservations",
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_quota_settlements"
TO ql3_runtime`,
],
(context, statement) => context.query(statement).then(() => undefined),
);
const POSTGRES_PRICE_QUOTE_TABLE_SQL = `
CREATE TABLE "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_price_quotes" (
invocation_id varchar(128) PRIMARY KEY,
project_id varchar(128) NOT NULL,
model_policy_revision varchar(128) NOT NULL,
provider varchar(128) NOT NULL,
model varchar(128) NOT NULL,
price_revision varchar(128) NOT NULL,
currency varchar(3) NOT NULL,
input_micros_per_million_tokens bigint NOT NULL,
output_micros_per_million_tokens bigint NOT NULL,
max_total_tokens bigint NOT NULL,
max_output_tokens bigint NOT NULL,
reserved_cost_micros bigint NOT NULL,
catalog_digest char(64) NOT NULL,
quote_digest char(64) NOT NULL,
record_json jsonb NOT NULL,
FOREIGN KEY (invocation_id)
REFERENCES "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_starts"
(invocation_id) ON DELETE RESTRICT,
CONSTRAINT ql3_model_invocation_price_quote_identity_check CHECK (
project_id ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
model_policy_revision ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
provider ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
model ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$' AND
price_revision ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$'
),
CONSTRAINT ql3_model_invocation_price_quote_value_check CHECK (
currency = 'USD' AND
input_micros_per_million_tokens BETWEEN 0 AND 1000000000000 AND
output_micros_per_million_tokens BETWEEN 0 AND 1000000000000 AND
max_total_tokens BETWEEN 1 AND 1000000000000 AND
max_output_tokens BETWEEN 1 AND max_total_tokens AND
reserved_cost_micros BETWEEN 0 AND 1000000000000000 AND
catalog_digest ~ '^[0-9a-f]{64}$' AND
quote_digest ~ '^[0-9a-f]{64}$'
),
CONSTRAINT ql3_model_invocation_price_quote_json_check CHECK (
jsonb_typeof(record_json) = 'object' AND
octet_length(record_json::text) BETWEEN 2 AND 24576 AND
record_json @> jsonb_build_object(
'schema', 'qinglong/model-invocation-price-quote@v1',
'invocationId', invocation_id,
'projectId', project_id,
'modelPolicyRevision', model_policy_revision,
'provider', provider,
'model', model,
'priceRevision', price_revision,
'currency', currency,
'inputMicrosPerMillionTokens', input_micros_per_million_tokens,
'outputMicrosPerMillionTokens', output_micros_per_million_tokens,
'maxTotalTokens', max_total_tokens,
'maxOutputTokens', max_output_tokens,
'reservedCostMicros', reserved_cost_micros,
'catalogDigest', catalog_digest,
'quoteDigest', quote_digest
)
)
)`;
const POSTGRES_PRICE_SETTLEMENT_TABLE_SQL = `
CREATE TABLE "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_price_settlements" (
invocation_id varchar(128) PRIMARY KEY,
project_id varchar(128) NOT NULL,
quote_digest char(64) NOT NULL,
completion_digest char(64) NOT NULL,
currency varchar(3) NOT NULL,
input_tokens bigint NOT NULL,
output_tokens bigint NOT NULL,
cost_micros bigint NOT NULL,
settled_at_ms bigint NOT NULL,
settlement_digest char(64) NOT NULL,
record_json jsonb NOT NULL,
FOREIGN KEY (invocation_id, quote_digest)
REFERENCES "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_price_quotes"
(invocation_id, quote_digest) ON DELETE RESTRICT,
FOREIGN KEY (invocation_id, completion_digest)
REFERENCES "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_completions"
(invocation_id, completion_digest) ON DELETE RESTRICT,
CONSTRAINT ql3_model_invocation_price_settlement_value_check CHECK (
currency = 'USD' AND
input_tokens >= 0 AND output_tokens >= 0 AND
cost_micros BETWEEN 0 AND 1000000000000000 AND
settled_at_ms >= 0 AND
quote_digest ~ '^[0-9a-f]{64}$' AND
completion_digest ~ '^[0-9a-f]{64}$' AND
settlement_digest ~ '^[0-9a-f]{64}$'
),
CONSTRAINT ql3_model_invocation_price_settlement_json_check CHECK (
jsonb_typeof(record_json) = 'object' AND
octet_length(record_json::text) BETWEEN 2 AND 24576 AND
record_json @> jsonb_build_object(
'schema', 'qinglong/model-invocation-price-settlement@v1',
'invocationId', invocation_id,
'projectId', project_id,
'quoteDigest', quote_digest,
'completionDigest', completion_digest,
'currency', currency,
'inputTokens', input_tokens,
'outputTokens', output_tokens,
'costMicros', cost_micros,
'settledAtMs', settled_at_ms,
'settlementDigest', settlement_digest
)
)
)`;
const postgresPricingMigration = defineSqlMigration<PostgresQueryable>(
POSTGRES_MODEL_INVOCATION_PRICING_MIGRATION_ID,
[
POSTGRES_PRICE_QUOTE_TABLE_SQL,
`CREATE UNIQUE INDEX ql3_model_invocation_price_quote_identity_digest_uidx
ON "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_price_quotes"
(invocation_id, quote_digest)`,
`CREATE UNIQUE INDEX ql3_model_invocation_price_quote_digest_uidx
ON "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_price_quotes"
(quote_digest)`,
POSTGRES_PRICE_SETTLEMENT_TABLE_SQL,
`CREATE UNIQUE INDEX ql3_model_invocation_price_settlement_digest_uidx
ON "${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_price_settlements"
(settlement_digest)`,
`REVOKE ALL ON TABLE
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_price_quotes",
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_price_settlements"
FROM PUBLIC`,
`GRANT SELECT, INSERT ON TABLE
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_price_quotes",
"${POSTGRES_MODEL_INVOCATION_SCHEMA}"."model_invocation_price_settlements"
TO ql3_runtime`,
],
(context, statement) => context.query(statement).then(() => undefined),
);
export const postgresUsagePricingMigrations = Object.freeze([
postgresUsageMigration,
postgresQuotaMigration,
postgresPricingMigration,
]);
@@ -0,0 +1,94 @@
import { createHash } from 'node:crypto';
import {
MigrationStreamHistoryCorruptionError,
type MigrationStreamRecord,
type MigrationStreamStep,
} from '@qinglong/runtime-core/migration-stream';
export interface HistoryRow extends Record<string, unknown> {
readonly migrationId: unknown;
readonly streamId: unknown;
readonly dialect: unknown;
readonly checksum: unknown;
readonly appliedAtMs: unknown;
}
function checksum(statements: readonly string[]): string {
return createHash('sha256')
.update(
JSON.stringify({
format: 1,
statements,
}),
)
.digest('hex');
}
export function defineSqlMigration<TContext>(
id: string,
statementsValue: readonly string[],
execute: (context: TContext, statement: string) => void | Promise<void>,
): MigrationStreamStep<TContext> {
const statements = Object.freeze(
statementsValue.map((statement) => statement.trim()),
);
if (
statements.length < 1 ||
statements.length > 32 ||
statements.some(
(statement) => statement.length < 1 || statement.length > 64 * 1024,
)
) {
throw new TypeError('ModelInvocation migration statements are invalid');
}
return Object.freeze({
id,
checksum: checksum(statements),
async up(context: TContext): Promise<void> {
for (const statement of statements) await execute(context, statement);
},
});
}
function appliedAtMs(value: unknown, migrationId: string): number {
if (typeof value === 'number' && Number.isSafeInteger(value) && value >= 0) {
return value;
}
if (typeof value === 'string' && /^(0|[1-9]\d*)$/.test(value)) {
const parsed = Number(value);
if (Number.isSafeInteger(parsed)) return parsed;
}
throw new MigrationStreamHistoryCorruptionError(migrationId);
}
export function historyRecord(
row: HistoryRow,
expected: Readonly<{
migrationIds: readonly string[];
streamId: string;
dialect: 'sqlite' | 'postgresql';
}>,
): MigrationStreamRecord {
const corruptionId =
typeof row.migrationId === 'string'
? row.migrationId
: expected.migrationIds[0] ?? expected.streamId;
if (
typeof row.migrationId !== 'string' ||
!expected.migrationIds.includes(row.migrationId) ||
row.streamId !== expected.streamId ||
row.dialect !== expected.dialect ||
typeof row.checksum !== 'string' ||
!/^[0-9a-f]{64}$/.test(row.checksum)
) {
throw new MigrationStreamHistoryCorruptionError(corruptionId);
}
return Object.freeze({
migrationId: row.migrationId,
streamId: row.streamId,
dialect: expected.dialect,
checksum: row.checksum,
appliedAtMs: appliedAtMs(row.appliedAtMs, row.migrationId),
});
}
@@ -0,0 +1,320 @@
import { Buffer } from 'node:buffer';
import { createHash } from 'node:crypto';
import type { DatabaseSync } from 'node:sqlite';
import {
MigrationStreamHistoryCorruptionError,
auditMigrationStreamHistory,
runMigrationStream,
type MigrationStreamDefinition,
type MigrationStreamRecord,
type MigrationStreamStore,
type MigrationStreamTransaction,
} from '@qinglong/runtime-core/migration-stream';
import {
LOCAL_MODEL_INVOCATION_MIGRATION_STREAM_ID,
LOCAL_MODEL_INVOCATION_MIGRATION_ID,
LOCAL_MODEL_INVOCATION_USAGE_MIGRATION_ID,
LOCAL_MODEL_INVOCATION_QUOTA_MIGRATION_ID,
LOCAL_MODEL_INVOCATION_PRICING_MIGRATION_ID,
LOCAL_MODEL_PRICE_CATALOG_MIGRATION_ID,
LOCAL_MODEL_PRICE_CATALOG_AUTHORIZATION_MIGRATION_ID,
LOCAL_MODEL_INVOCATION_FEATURE_ACTIVATION_MIGRATION_ID,
LOCAL_PLUGIN_PACKAGE_PROMPT_ADMISSION_MIGRATION_ID,
LOCAL_PLUGIN_PACKAGE_PROMPT_FINALIZATION_MIGRATION_ID,
LOCAL_PLUGIN_PACKAGE_PROMPT_OUTPUT_ARTIFACT_MIGRATION_ID,
LOCAL_PLUGIN_PACKAGE_PROMPT_OUTPUT_TOMBSTONE_MIGRATION_ID,
LOCAL_PLUGIN_PACKAGE_PROMPT_OUTPUT_KEY_RETIREMENT_MIGRATION_ID,
LOCAL_MODEL_PROVIDER_CREDENTIAL_CATALOG_MIGRATION_ID,
LOCAL_MODEL_INVOCATION_MIGRATION_HISTORY_TABLE,
} from './identities';
import { historyRecord, type HistoryRow } from './shared';
import { sqliteCatalogMigrations } from './sqlite/catalog';
import type { LocalMigrationContext } from './sqlite/context';
import { sqliteCoreMigrations } from './sqlite/core';
import { sqliteCredentialMigrations } from './sqlite/credential';
import { sqlitePromptMigrations } from './sqlite/prompt';
import { sqliteUsagePricingMigrations } from './sqlite/usagePricing';
const LOCAL_HISTORY_IDENTITY = Object.freeze({
migrationIds: Object.freeze([
LOCAL_MODEL_INVOCATION_MIGRATION_ID,
LOCAL_MODEL_INVOCATION_USAGE_MIGRATION_ID,
LOCAL_MODEL_INVOCATION_QUOTA_MIGRATION_ID,
LOCAL_MODEL_INVOCATION_PRICING_MIGRATION_ID,
LOCAL_MODEL_PRICE_CATALOG_MIGRATION_ID,
LOCAL_MODEL_PRICE_CATALOG_AUTHORIZATION_MIGRATION_ID,
LOCAL_MODEL_INVOCATION_FEATURE_ACTIVATION_MIGRATION_ID,
LOCAL_PLUGIN_PACKAGE_PROMPT_ADMISSION_MIGRATION_ID,
LOCAL_PLUGIN_PACKAGE_PROMPT_FINALIZATION_MIGRATION_ID,
LOCAL_PLUGIN_PACKAGE_PROMPT_OUTPUT_ARTIFACT_MIGRATION_ID,
LOCAL_PLUGIN_PACKAGE_PROMPT_OUTPUT_TOMBSTONE_MIGRATION_ID,
LOCAL_PLUGIN_PACKAGE_PROMPT_OUTPUT_KEY_RETIREMENT_MIGRATION_ID,
LOCAL_MODEL_PROVIDER_CREDENTIAL_CATALOG_MIGRATION_ID,
]),
streamId: LOCAL_MODEL_INVOCATION_MIGRATION_STREAM_ID,
dialect: 'sqlite' as const,
});
function readLocalHistory(
client: DatabaseSync,
migrationId?: string,
): readonly MigrationStreamRecord[] {
const rows = client
.prepare(
`SELECT
migration_id AS "migrationId",
stream_id AS "streamId",
dialect,
checksum,
applied_at_ms AS "appliedAtMs"
FROM "${LOCAL_MODEL_INVOCATION_MIGRATION_HISTORY_TABLE}"
WHERE stream_id = ?
${migrationId === undefined ? '' : 'AND migration_id = ?'}
ORDER BY migration_id`,
)
.all(
LOCAL_MODEL_INVOCATION_MIGRATION_STREAM_ID,
...(migrationId === undefined ? [] : [migrationId]),
) as unknown as HistoryRow[];
return rows.map((row) => historyRecord(row, LOCAL_HISTORY_IDENTITY));
}
class LocalModelInvocationMigrationStore
implements MigrationStreamStore<LocalMigrationContext>
{
constructor(private readonly client: DatabaseSync) {}
async ensureHistory(): Promise<void> {
const required = [
'QingLong3SchemaMigrations',
'Runs',
'RunEvents',
'StepRuns',
'StepRunMutations',
];
const rows = this.client
.prepare(
`SELECT name FROM sqlite_schema
WHERE type = 'table' AND name IN (${required
.map(() => '?')
.join(', ')})`,
)
.all(...required) as unknown as { name?: unknown }[];
const names = new Set(
rows
.map((row) => row.name)
.filter((name): name is string => typeof name === 'string'),
);
if (required.some((name) => !names.has(name))) {
throw new TypeError(
'Local ModelInvocation feature requires the main SQLite migration stream',
);
}
this.client.exec(`
CREATE TABLE IF NOT EXISTS "${LOCAL_MODEL_INVOCATION_MIGRATION_HISTORY_TABLE}" (
migration_id TEXT PRIMARY KEY,
stream_id TEXT NOT NULL
CONSTRAINT ql3_ai_migrations_stream_check
CHECK (stream_id = '${LOCAL_MODEL_INVOCATION_MIGRATION_STREAM_ID}'),
dialect TEXT NOT NULL
CONSTRAINT ql3_ai_migrations_dialect_check CHECK (dialect = 'sqlite'),
checksum TEXT NOT NULL
CONSTRAINT ql3_ai_migrations_checksum_check
CHECK (length(checksum) = 64 AND checksum NOT GLOB '*[^0-9a-f]*'),
applied_at_ms INTEGER NOT NULL
CONSTRAINT ql3_ai_migrations_applied_at_check CHECK (applied_at_ms >= 0)
)
`);
}
async listAll(): Promise<readonly MigrationStreamRecord[]> {
return readLocalHistory(this.client);
}
async findById(migrationId: string): Promise<MigrationStreamRecord | null> {
const rows = readLocalHistory(this.client, migrationId);
if (rows.length > 1) {
throw new MigrationStreamHistoryCorruptionError(migrationId);
}
return rows[0] ?? null;
}
async transaction<T>(
work: (
transaction: MigrationStreamTransaction<LocalMigrationContext>,
) => Promise<T>,
): Promise<T> {
this.client.exec('BEGIN IMMEDIATE');
try {
const result = await work({
context: { client: this.client },
findById: (migrationId) => this.findById(migrationId),
insert: async (record) => {
if (
record.streamId !== LOCAL_MODEL_INVOCATION_MIGRATION_STREAM_ID ||
!LOCAL_HISTORY_IDENTITY.migrationIds.includes(record.migrationId) ||
record.dialect !== 'sqlite'
) {
throw new TypeError(
'Local ModelInvocation migration identity is invalid',
);
}
this.client
.prepare(
`INSERT INTO "${LOCAL_MODEL_INVOCATION_MIGRATION_HISTORY_TABLE}"
(migration_id, stream_id, dialect, checksum, applied_at_ms)
VALUES (?, ?, ?, ?, ?)`,
)
.run(
record.migrationId,
record.streamId,
record.dialect,
record.checksum,
record.appliedAtMs,
);
},
});
this.client.exec('COMMIT');
return result;
} catch (error) {
if (this.client.isTransaction) this.client.exec('ROLLBACK');
throw error;
}
}
}
export const localModelInvocationMigrationDefinition: MigrationStreamDefinition<LocalMigrationContext> =
Object.freeze({
id: LOCAL_MODEL_INVOCATION_MIGRATION_STREAM_ID,
dialect: 'sqlite',
migrationIdScheme: 'sqlite-numbered',
checksumScheme: 'sha256',
migrations: Object.freeze([
...sqliteCoreMigrations,
...sqliteUsagePricingMigrations,
...sqliteCatalogMigrations,
...sqlitePromptMigrations,
...sqliteCredentialMigrations,
]),
});
export const LOCAL_MODEL_INVOCATION_MIGRATION_PLAN_DIGEST = createHash('sha256')
.update(
Buffer.from('qinglong/local-model-invocation-migration-plan@v1\0', 'utf8'),
)
.update(
JSON.stringify(
localModelInvocationMigrationDefinition.migrations.map(
({ id, checksum: migrationChecksum }) => ({
id,
checksum: migrationChecksum,
}),
),
),
'utf8',
)
.digest('hex');
export class LocalModelInvocationFeatureNotReadyError extends Error {
readonly code = 'LOCAL_MODEL_INVOCATION_FEATURE_NOT_READY';
constructor(options?: ErrorOptions) {
super('The local ModelInvocation feature schema is not ready', options);
this.name = 'LocalModelInvocationFeatureNotReadyError';
}
}
const LOCAL_MODEL_INVOCATION_FEATURE_TABLES = Object.freeze([
'ModelInvocationCompletions',
'ModelInvocationFeatureHead',
'ModelInvocationFeatureTransitions',
'ModelInvocationPriceQuotes',
'ModelInvocationPriceSettlements',
'ModelInvocationPromptAdmissions',
'ModelInvocationPromptFinalizations',
'ModelInvocationPromptOutputArtifactTombstones',
'ModelInvocationPromptOutputArtifacts',
'ModelInvocationPromptOutputKeyRetirementCompletions',
'ModelInvocationPromptOutputKeyRetirementPreparations',
'ModelInvocationProviderCredentialAudits',
'ModelInvocationProviderCredentialBindings',
'ModelInvocationProviderCredentialTransitions',
'ModelInvocationQuotaReservations',
'ModelInvocationQuotaSettlements',
'ModelInvocationResolutions',
'ModelInvocationStarts',
'ModelInvocationUsageLedger',
'ModelPriceCatalogAuthorizations',
'ModelPriceCatalogHeads',
'ModelPriceCatalogPublications',
LOCAL_MODEL_INVOCATION_MIGRATION_HISTORY_TABLE,
]);
export function assertLocalModelInvocationFeatureReady(
client: DatabaseSync,
): void {
try {
if (
!client ||
typeof client !== 'object' ||
!client.isOpen ||
typeof client.prepare !== 'function'
) {
throw new LocalModelInvocationFeatureNotReadyError();
}
const tables = (
client
.prepare(
`SELECT name
FROM sqlite_schema
WHERE type = 'table'
AND (
name = ?
OR name LIKE 'ModelInvocation%'
OR name LIKE 'ModelPriceCatalog%'
)
ORDER BY name`,
)
.all(LOCAL_MODEL_INVOCATION_MIGRATION_HISTORY_TABLE) as {
readonly name?: unknown;
}[]
).map(({ name }) => name);
if (
tables.length !== LOCAL_MODEL_INVOCATION_FEATURE_TABLES.length ||
tables.some(
(table, index) =>
table !== LOCAL_MODEL_INVOCATION_FEATURE_TABLES[index],
)
) {
throw new LocalModelInvocationFeatureNotReadyError();
}
const applied = auditMigrationStreamHistory(
readLocalHistory(client),
localModelInvocationMigrationDefinition,
);
if (
applied.size !== localModelInvocationMigrationDefinition.migrations.length
) {
throw new LocalModelInvocationFeatureNotReadyError();
}
} catch (error) {
if (error instanceof LocalModelInvocationFeatureNotReadyError) {
throw error;
}
throw new LocalModelInvocationFeatureNotReadyError({
cause: error instanceof Error ? error : undefined,
});
}
}
export async function migrateLocalModelInvocationFeature(
client: DatabaseSync,
): Promise<void> {
await runMigrationStream({
stream: localModelInvocationMigrationDefinition,
store: new LocalModelInvocationMigrationStore(client),
});
}
@@ -0,0 +1,486 @@
import type { LocalMigrationContext } from './context';
import {
LOCAL_MODEL_PRICE_CATALOG_MIGRATION_ID,
LOCAL_MODEL_PRICE_CATALOG_AUTHORIZATION_MIGRATION_ID,
LOCAL_MODEL_INVOCATION_FEATURE_ACTIVATION_MIGRATION_ID,
} from '../identities';
import { defineSqlMigration } from '../shared';
const LOCAL_MODEL_PRICE_CATALOG_PUBLICATION_TABLE_SQL = `
CREATE TABLE "ModelPriceCatalogPublications" (
provider TEXT NOT NULL,
model TEXT NOT NULL,
price_revision TEXT NOT NULL,
catalog_digest TEXT NOT NULL,
mutation_id TEXT NOT NULL UNIQUE,
command_digest TEXT NOT NULL,
publication_digest TEXT NOT NULL,
published_at_ms INTEGER NOT NULL,
published_by_user_id TEXT NOT NULL,
publication_json TEXT NOT NULL,
PRIMARY KEY (provider, model, price_revision),
CONSTRAINT ql3_model_price_catalog_publication_identity_check CHECK (
length(provider) BETWEEN 1 AND 128 AND
length(model) BETWEEN 1 AND 128 AND
length(price_revision) BETWEEN 1 AND 128 AND
length(mutation_id) BETWEEN 1 AND 128 AND
length(published_by_user_id) BETWEEN 1 AND 128
),
CONSTRAINT ql3_model_price_catalog_publication_value_check CHECK (
published_at_ms >= 0 AND
length(catalog_digest) = 64 AND
catalog_digest NOT GLOB '*[^0-9a-f]*' AND
length(command_digest) = 64 AND
command_digest NOT GLOB '*[^0-9a-f]*' AND
length(publication_digest) = 64 AND
publication_digest NOT GLOB '*[^0-9a-f]*'
),
CONSTRAINT ql3_model_price_catalog_publication_json_check CHECK (
length(CAST(publication_json AS BLOB)) BETWEEN 2 AND 24576 AND
json_valid(publication_json) AND
json_type(publication_json) = 'object' AND
json_extract(publication_json, '$.schema') =
'qinglong/model-price-catalog-publication@v1' AND
json_extract(publication_json, '$.entry.schema') =
'qinglong/model-price-catalog-entry@v1' AND
json_extract(publication_json, '$.entry.provider') = provider AND
json_extract(publication_json, '$.entry.model') = model AND
json_extract(publication_json, '$.entry.priceRevision') =
price_revision AND
json_extract(publication_json, '$.entry.catalogDigest') =
catalog_digest AND
json_extract(publication_json, '$.entry.publishedAtMs') =
published_at_ms AND
json_extract(publication_json, '$.mutationId') = mutation_id AND
json_extract(publication_json, '$.publishedByUserId') =
published_by_user_id AND
json_extract(publication_json, '$.commandDigest') = command_digest AND
json_extract(publication_json, '$.publicationDigest') =
publication_digest
)
)`;
const LOCAL_MODEL_PRICE_CATALOG_HEAD_TABLE_SQL = `
CREATE TABLE "ModelPriceCatalogHeads" (
provider TEXT NOT NULL,
model TEXT NOT NULL,
generation INTEGER NOT NULL,
previous_head_digest TEXT,
active_price_revision TEXT,
active_catalog_digest TEXT,
revoked_price_revision TEXT,
revoked_catalog_digest TEXT,
action TEXT NOT NULL,
mutation_id TEXT NOT NULL UNIQUE,
changed_by_user_id TEXT NOT NULL,
changed_at_ms INTEGER NOT NULL,
command_digest TEXT NOT NULL,
head_digest TEXT NOT NULL UNIQUE,
head_json TEXT NOT NULL,
PRIMARY KEY (provider, model, generation),
FOREIGN KEY (previous_head_digest)
REFERENCES "ModelPriceCatalogHeads" (head_digest) ON DELETE RESTRICT,
FOREIGN KEY (
provider, model, active_price_revision, active_catalog_digest
) REFERENCES "ModelPriceCatalogPublications" (
provider, model, price_revision, catalog_digest
) ON DELETE RESTRICT,
FOREIGN KEY (
provider, model, revoked_price_revision, revoked_catalog_digest
) REFERENCES "ModelPriceCatalogPublications" (
provider, model, price_revision, catalog_digest
) ON DELETE RESTRICT,
CONSTRAINT ql3_model_price_catalog_head_identity_check CHECK (
length(provider) BETWEEN 1 AND 128 AND
length(model) BETWEEN 1 AND 128 AND
length(mutation_id) BETWEEN 1 AND 128 AND
length(changed_by_user_id) BETWEEN 1 AND 128
),
CONSTRAINT ql3_model_price_catalog_head_value_check CHECK (
generation BETWEEN 1 AND 2147483647 AND
((generation = 1 AND previous_head_digest IS NULL) OR
(generation > 1 AND previous_head_digest IS NOT NULL)) AND
((active_price_revision IS NULL AND active_catalog_digest IS NULL) OR
(active_price_revision IS NOT NULL AND
active_catalog_digest IS NOT NULL)) AND
((revoked_price_revision IS NULL AND revoked_catalog_digest IS NULL) OR
(revoked_price_revision IS NOT NULL AND
revoked_catalog_digest IS NOT NULL)) AND
(revoked_price_revision IS NULL OR
revoked_price_revision <> active_price_revision) AND
action IN ('activate', 'deactivate', 'revoke') AND
((action = 'revoke') = (revoked_price_revision IS NOT NULL)) AND
changed_at_ms >= 0 AND
(previous_head_digest IS NULL OR (
length(previous_head_digest) = 64 AND
previous_head_digest NOT GLOB '*[^0-9a-f]*'
)) AND
(active_catalog_digest IS NULL OR (
length(active_catalog_digest) = 64 AND
active_catalog_digest NOT GLOB '*[^0-9a-f]*'
)) AND
(revoked_catalog_digest IS NULL OR (
length(revoked_catalog_digest) = 64 AND
revoked_catalog_digest NOT GLOB '*[^0-9a-f]*'
)) AND
length(command_digest) = 64 AND
command_digest NOT GLOB '*[^0-9a-f]*' AND
length(head_digest) = 64 AND head_digest NOT GLOB '*[^0-9a-f]*'
),
CONSTRAINT ql3_model_price_catalog_head_json_check CHECK (
length(CAST(head_json AS BLOB)) BETWEEN 2 AND 24576 AND
json_valid(head_json) AND json_type(head_json) = 'object' AND
json_extract(head_json, '$.schema') =
'qinglong/model-price-catalog-head@v1' AND
json_extract(head_json, '$.provider') = provider AND
json_extract(head_json, '$.model') = model AND
json_extract(head_json, '$.generation') = generation AND
((previous_head_digest IS NULL AND
json_type(head_json, '$.previousHeadDigest') = 'null') OR
json_extract(head_json, '$.previousHeadDigest') =
previous_head_digest) AND
((active_price_revision IS NULL AND
json_type(head_json, '$.activePriceRevision') = 'null') OR
json_extract(head_json, '$.activePriceRevision') =
active_price_revision) AND
((active_catalog_digest IS NULL AND
json_type(head_json, '$.activeCatalogDigest') = 'null') OR
json_extract(head_json, '$.activeCatalogDigest') =
active_catalog_digest) AND
((revoked_price_revision IS NULL AND
json_type(head_json, '$.revokedPriceRevision') = 'null') OR
json_extract(head_json, '$.revokedPriceRevision') =
revoked_price_revision) AND
((revoked_catalog_digest IS NULL AND
json_type(head_json, '$.revokedCatalogDigest') = 'null') OR
json_extract(head_json, '$.revokedCatalogDigest') =
revoked_catalog_digest) AND
json_extract(head_json, '$.action') = action AND
json_extract(head_json, '$.mutationId') = mutation_id AND
json_extract(head_json, '$.changedByUserId') =
changed_by_user_id AND
json_extract(head_json, '$.changedAtMs') = changed_at_ms AND
json_extract(head_json, '$.commandDigest') = command_digest AND
json_extract(head_json, '$.headDigest') = head_digest
)
)`;
const localModelPriceCatalogMigration =
defineSqlMigration<LocalMigrationContext>(
LOCAL_MODEL_PRICE_CATALOG_MIGRATION_ID,
[
LOCAL_MODEL_PRICE_CATALOG_PUBLICATION_TABLE_SQL,
`CREATE UNIQUE INDEX ql3_model_price_catalog_publication_identity_digest_uidx
ON "ModelPriceCatalogPublications"
(provider, model, price_revision, catalog_digest)`,
`CREATE UNIQUE INDEX ql3_model_price_catalog_publication_digest_uidx
ON "ModelPriceCatalogPublications" (publication_digest)`,
LOCAL_MODEL_PRICE_CATALOG_HEAD_TABLE_SQL,
`CREATE INDEX ql3_model_price_catalog_head_current_idx
ON "ModelPriceCatalogHeads" (provider, model, generation DESC)`,
`CREATE UNIQUE INDEX ql3_model_price_catalog_revoked_revision_uidx
ON "ModelPriceCatalogHeads"
(provider, model, revoked_price_revision)
WHERE revoked_price_revision IS NOT NULL`,
],
(context, statement) => context.client.exec(statement),
);
const LOCAL_MODEL_PRICE_CATALOG_AUTHORIZATION_TABLE_SQL = `
CREATE TABLE "ModelPriceCatalogAuthorizations" (
authorization_id TEXT PRIMARY KEY,
request_id TEXT NOT NULL,
operation TEXT NOT NULL,
provider TEXT NOT NULL,
model TEXT NOT NULL,
price_revision TEXT,
catalog_command_digest TEXT NOT NULL UNIQUE,
publication_digest TEXT UNIQUE,
head_digest TEXT UNIQUE,
result_digest TEXT NOT NULL UNIQUE,
user_id TEXT NOT NULL,
authentication_id TEXT NOT NULL,
assurance TEXT NOT NULL,
authenticated_at_ms INTEGER NOT NULL,
expires_at_ms INTEGER NOT NULL,
policy_revision TEXT NOT NULL,
policy_decision_digest TEXT NOT NULL,
decision_mode TEXT NOT NULL,
command_digest TEXT NOT NULL UNIQUE,
committed_at_ms INTEGER NOT NULL,
authorization_digest TEXT NOT NULL UNIQUE,
reasons_json TEXT NOT NULL,
authorization_json TEXT NOT NULL,
FOREIGN KEY (publication_digest)
REFERENCES "ModelPriceCatalogPublications" (publication_digest)
ON DELETE RESTRICT,
FOREIGN KEY (head_digest)
REFERENCES "ModelPriceCatalogHeads" (head_digest)
ON DELETE RESTRICT,
CONSTRAINT ql3_model_price_catalog_authorization_identity_check CHECK (
length(authorization_id) BETWEEN 1 AND 128 AND
length(request_id) BETWEEN 1 AND 128 AND
length(provider) BETWEEN 1 AND 128 AND
length(model) BETWEEN 1 AND 128 AND
(price_revision IS NULL OR length(price_revision) BETWEEN 1 AND 128) AND
length(user_id) BETWEEN 1 AND 128 AND
length(authentication_id) BETWEEN 1 AND 128 AND
length(policy_revision) BETWEEN 1 AND 128
),
CONSTRAINT ql3_model_price_catalog_authorization_value_check CHECK (
operation IN ('publish', 'activate', 'deactivate', 'revoke') AND
((operation = 'deactivate' AND price_revision IS NULL) OR
(operation <> 'deactivate' AND price_revision IS NOT NULL)) AND
((operation = 'publish' AND publication_digest IS NOT NULL AND
head_digest IS NULL AND result_digest = publication_digest) OR
(operation <> 'publish' AND publication_digest IS NULL AND
head_digest IS NOT NULL AND result_digest = head_digest)) AND
assurance IN ('multi_factor', 'hardware', 'local_console') AND
decision_mode IN ('human_confirmation', 'separation_of_duty') AND
authenticated_at_ms >= 0 AND
expires_at_ms > authenticated_at_ms AND
committed_at_ms >= authenticated_at_ms AND
committed_at_ms < expires_at_ms AND
committed_at_ms - authenticated_at_ms <= 300000 AND
length(catalog_command_digest) = 64 AND
catalog_command_digest NOT GLOB '*[^0-9a-f]*' AND
length(result_digest) = 64 AND
result_digest NOT GLOB '*[^0-9a-f]*' AND
length(policy_decision_digest) = 64 AND
policy_decision_digest NOT GLOB '*[^0-9a-f]*' AND
length(command_digest) = 64 AND
command_digest NOT GLOB '*[^0-9a-f]*' AND
length(authorization_digest) = 64 AND
authorization_digest NOT GLOB '*[^0-9a-f]*'
),
CONSTRAINT ql3_model_price_catalog_authorization_reasons_check CHECK (
length(CAST(reasons_json AS BLOB)) BETWEEN 5 AND 2048 AND
json_valid(reasons_json) AND json_type(reasons_json) = 'array' AND
json_array_length(reasons_json) BETWEEN 1 AND 8
),
CONSTRAINT ql3_model_price_catalog_authorization_json_check CHECK (
length(CAST(authorization_json AS BLOB)) BETWEEN 2 AND 32768 AND
json_valid(authorization_json) AND
json_type(authorization_json) = 'object' AND
json_extract(authorization_json, '$.schema') =
'qinglong/model-price-catalog-authorization@v1' AND
json_extract(authorization_json, '$.authorizationId') =
authorization_id AND
json_extract(authorization_json, '$.requestId') = request_id AND
json_extract(authorization_json, '$.operation') = operation AND
json_extract(authorization_json, '$.provider') = provider AND
json_extract(authorization_json, '$.model') = model AND
((price_revision IS NULL AND
json_type(authorization_json, '$.priceRevision') = 'null') OR
json_extract(authorization_json, '$.priceRevision') =
price_revision) AND
json_extract(authorization_json, '$.catalogCommandDigest') =
catalog_command_digest AND
json_extract(authorization_json, '$.resultDigest') = result_digest AND
json_extract(authorization_json, '$.principal.subject.type') = 'user' AND
json_extract(authorization_json, '$.principal.subject.id') = user_id AND
json_extract(authorization_json, '$.principal.authenticationId') =
authentication_id AND
json_extract(authorization_json, '$.principal.assurance') = assurance AND
json_extract(authorization_json, '$.principal.authenticatedAtMs') =
authenticated_at_ms AND
json_extract(authorization_json, '$.principal.expiresAtMs') =
expires_at_ms AND
json_extract(authorization_json, '$.policy.revision') =
policy_revision AND
json_extract(authorization_json, '$.policy.decisionDigest') =
policy_decision_digest AND
json_extract(authorization_json, '$.decisionMode') = decision_mode AND
json_extract(authorization_json, '$.commandDigest') = command_digest AND
json_extract(authorization_json, '$.committedAtMs') = committed_at_ms AND
json_extract(authorization_json, '$.authorizationDigest') =
authorization_digest AND
json_extract(authorization_json, '$.policy.reasons') =
json(reasons_json)
)
)`;
const localModelPriceCatalogAuthorizationMigration =
defineSqlMigration<LocalMigrationContext>(
LOCAL_MODEL_PRICE_CATALOG_AUTHORIZATION_MIGRATION_ID,
[
LOCAL_MODEL_PRICE_CATALOG_AUTHORIZATION_TABLE_SQL,
`CREATE INDEX ql3_model_price_catalog_authorization_target_idx
ON "ModelPriceCatalogAuthorizations"
(provider, model, operation, committed_at_ms DESC)`,
],
(context, statement) => context.client.exec(statement),
);
const LOCAL_MODEL_INVOCATION_FEATURE_TRANSITION_TABLE_SQL = `
CREATE TABLE "ModelInvocationFeatureTransitions" (
feature_id TEXT NOT NULL
CONSTRAINT ql3_ai_feature_transition_feature_check
CHECK (feature_id = 'model-invocation'),
generation INTEGER NOT NULL
CONSTRAINT ql3_ai_feature_transition_generation_check
CHECK (generation BETWEEN 1 AND 2147483647),
previous_generation INTEGER,
state TEXT NOT NULL
CONSTRAINT ql3_ai_feature_transition_state_check
CHECK (state IN ('active', 'inactive')),
mutation_id TEXT NOT NULL UNIQUE,
request_id TEXT NOT NULL,
expected_migration_digest TEXT NOT NULL,
safety_mode TEXT NOT NULL
CONSTRAINT ql3_ai_feature_transition_safety_check
CHECK (safety_mode IN (
'fresh_database',
'backup_verified',
'preserve_existing'
)),
backup_evidence_digest TEXT,
changed_by_user_id TEXT NOT NULL,
authentication_id TEXT NOT NULL,
assurance TEXT NOT NULL
CONSTRAINT ql3_ai_feature_transition_assurance_check
CHECK (assurance = 'local_console'),
command_digest TEXT NOT NULL,
transition_digest TEXT NOT NULL UNIQUE,
committed_at_ms INTEGER NOT NULL
CONSTRAINT ql3_ai_feature_transition_time_check
CHECK (committed_at_ms >= 0),
transition_json TEXT NOT NULL,
PRIMARY KEY (feature_id, generation),
UNIQUE (feature_id, generation, transition_digest),
CONSTRAINT ql3_ai_feature_transition_previous_fk
FOREIGN KEY (feature_id, previous_generation)
REFERENCES "ModelInvocationFeatureTransitions" (feature_id, generation)
ON DELETE RESTRICT,
CONSTRAINT ql3_ai_feature_transition_previous_check CHECK (
(
generation = 1 AND
previous_generation IS NULL AND
state = 'active'
) OR (
generation > 1 AND
previous_generation = generation - 1
)
),
CONSTRAINT ql3_ai_feature_transition_identity_check CHECK (
length(mutation_id) BETWEEN 1 AND 128 AND
length(request_id) BETWEEN 1 AND 128 AND
length(changed_by_user_id) BETWEEN 1 AND 255 AND
length(authentication_id) BETWEEN 1 AND 128
),
CONSTRAINT ql3_ai_feature_transition_digest_check CHECK (
length(expected_migration_digest) = 64 AND
expected_migration_digest NOT GLOB '*[^0-9a-f]*' AND
(
backup_evidence_digest IS NULL OR
length(backup_evidence_digest) = 64 AND
backup_evidence_digest NOT GLOB '*[^0-9a-f]*'
) AND
length(command_digest) = 64 AND
command_digest NOT GLOB '*[^0-9a-f]*' AND
length(transition_digest) = 64 AND
transition_digest NOT GLOB '*[^0-9a-f]*'
),
CONSTRAINT ql3_ai_feature_transition_safety_shape_check CHECK (
(
state = 'active' AND
(
(
safety_mode = 'fresh_database' AND
backup_evidence_digest IS NULL
) OR (
safety_mode = 'backup_verified' AND
backup_evidence_digest IS NOT NULL
)
)
) OR (
state = 'inactive' AND
safety_mode = 'preserve_existing' AND
backup_evidence_digest IS NULL
)
),
CONSTRAINT ql3_ai_feature_transition_json_check CHECK (
json_valid(transition_json) AND
json_type(transition_json) = 'object' AND
json_extract(transition_json, '$.schema') =
'qinglong/model-invocation-feature-transition@v1' AND
json_extract(transition_json, '$.featureId') = feature_id AND
json_extract(transition_json, '$.generation') = generation AND
(
(
previous_generation IS NULL AND
json_type(transition_json, '$.previousGeneration') = 'null'
) OR
json_extract(transition_json, '$.previousGeneration') =
previous_generation
) AND
json_extract(transition_json, '$.state') = state AND
json_extract(transition_json, '$.mutationId') = mutation_id AND
json_extract(transition_json, '$.requestId') = request_id AND
json_extract(transition_json, '$.expectedMigrationDigest') =
expected_migration_digest AND
json_extract(transition_json, '$.safety.mode') = safety_mode AND
(
(
backup_evidence_digest IS NULL AND
json_type(transition_json, '$.safety.backupEvidenceDigest') = 'null'
) OR
json_extract(transition_json, '$.safety.backupEvidenceDigest') =
backup_evidence_digest
) AND
json_extract(transition_json, '$.changedByUserId') =
changed_by_user_id AND
json_extract(transition_json, '$.authenticationId') =
authentication_id AND
json_extract(transition_json, '$.assurance') = assurance AND
json_extract(transition_json, '$.commandDigest') = command_digest AND
json_extract(transition_json, '$.committedAtMs') = committed_at_ms AND
json_extract(transition_json, '$.transitionDigest') =
transition_digest
)
)`;
const LOCAL_MODEL_INVOCATION_FEATURE_HEAD_TABLE_SQL = `
CREATE TABLE "ModelInvocationFeatureHead" (
feature_id TEXT PRIMARY KEY NOT NULL
CONSTRAINT ql3_ai_feature_head_feature_check
CHECK (feature_id = 'model-invocation'),
generation INTEGER NOT NULL
CONSTRAINT ql3_ai_feature_head_generation_check
CHECK (generation BETWEEN 1 AND 2147483647),
state TEXT NOT NULL
CONSTRAINT ql3_ai_feature_head_state_check
CHECK (state IN ('active', 'inactive')),
transition_digest TEXT NOT NULL UNIQUE,
updated_at_ms INTEGER NOT NULL
CONSTRAINT ql3_ai_feature_head_time_check
CHECK (updated_at_ms >= 0),
CONSTRAINT ql3_ai_feature_head_transition_fk
FOREIGN KEY (feature_id, generation, transition_digest)
REFERENCES "ModelInvocationFeatureTransitions" (
feature_id,
generation,
transition_digest
) ON DELETE RESTRICT
)`;
const localModelInvocationFeatureActivationMigration =
defineSqlMigration<LocalMigrationContext>(
LOCAL_MODEL_INVOCATION_FEATURE_ACTIVATION_MIGRATION_ID,
[
LOCAL_MODEL_INVOCATION_FEATURE_TRANSITION_TABLE_SQL,
`CREATE INDEX ql3_ai_feature_transition_state_idx
ON "ModelInvocationFeatureTransitions"
(feature_id, state, generation DESC)`,
LOCAL_MODEL_INVOCATION_FEATURE_HEAD_TABLE_SQL,
],
(context, statement) => context.client.exec(statement),
);
export const sqliteCatalogMigrations = Object.freeze([
localModelPriceCatalogMigration,
localModelPriceCatalogAuthorizationMigration,
localModelInvocationFeatureActivationMigration,
]);
@@ -0,0 +1,5 @@
import type { DatabaseSync } from 'node:sqlite';
export interface LocalMigrationContext {
readonly client: DatabaseSync;
}
@@ -0,0 +1,249 @@
import type { LocalMigrationContext } from './context';
import {
LOCAL_MODEL_INVOCATION_MIGRATION_ID,
} from '../identities';
import { defineSqlMigration } from '../shared';
const LOCAL_START_TABLE_SQL = `
CREATE TABLE "ModelInvocationStarts" (
invocation_id TEXT PRIMARY KEY NOT NULL,
project_id TEXT NOT NULL,
run_id TEXT NOT NULL,
step_run_id TEXT NOT NULL,
trace_id TEXT NOT NULL,
provider TEXT NOT NULL,
model TEXT NOT NULL,
policy_revision TEXT NOT NULL,
request_digest TEXT NOT NULL,
input_bytes INTEGER NOT NULL,
max_output_tokens INTEGER NOT NULL,
deadline_at_ms INTEGER NOT NULL,
admitted_at_ms INTEGER NOT NULL,
mutation_id TEXT NOT NULL,
mutation_digest TEXT NOT NULL,
run_event_id TEXT NOT NULL,
start_digest TEXT NOT NULL,
record_json TEXT NOT NULL,
FOREIGN KEY (run_id, step_run_id)
REFERENCES "StepRuns" (run_id, id) ON DELETE RESTRICT,
FOREIGN KEY (mutation_id)
REFERENCES "StepRunMutations" (mutation_id) ON DELETE RESTRICT,
FOREIGN KEY (run_event_id)
REFERENCES "RunEvents" (id) ON DELETE RESTRICT,
CONSTRAINT ql3_model_invocation_starts_identity_check CHECK (
length(invocation_id) BETWEEN 1 AND 128 AND
length(project_id) BETWEEN 1 AND 128 AND
length(run_id) BETWEEN 1 AND 128 AND
length(step_run_id) BETWEEN 1 AND 128 AND
length(trace_id) BETWEEN 1 AND 128 AND
length(provider) BETWEEN 1 AND 128 AND
length(model) BETWEEN 1 AND 128 AND
length(policy_revision) BETWEEN 1 AND 128 AND
length(mutation_id) BETWEEN 1 AND 128 AND
length(run_event_id) BETWEEN 1 AND 36
),
CONSTRAINT ql3_model_invocation_starts_digest_check CHECK (
length(request_digest) = 71 AND
substr(request_digest, 1, 7) = 'sha256:' AND
substr(request_digest, 8) NOT GLOB '*[^0-9a-f]*' AND
length(mutation_digest) = 64 AND
mutation_digest NOT GLOB '*[^0-9a-f]*' AND
length(start_digest) = 64 AND
start_digest NOT GLOB '*[^0-9a-f]*'
),
CONSTRAINT ql3_model_invocation_starts_budget_check CHECK (
input_bytes BETWEEN 1 AND 262144 AND
max_output_tokens BETWEEN 1 AND 32768 AND
admitted_at_ms >= 0 AND deadline_at_ms > admitted_at_ms
),
CONSTRAINT ql3_model_invocation_starts_json_check CHECK (
length(CAST(record_json AS BLOB)) BETWEEN 2 AND 24576 AND
json_valid(record_json) AND json_type(record_json) = 'object' AND
json_extract(record_json, '$.schema') =
'qinglong/model-invocation-start@v1' AND
json_extract(record_json, '$.invocationId') = invocation_id AND
json_extract(record_json, '$.projectId') = project_id AND
json_extract(record_json, '$.runId') = run_id AND
json_extract(record_json, '$.stepRunId') = step_run_id AND
json_extract(record_json, '$.traceId') = trace_id AND
json_extract(record_json, '$.provider') = provider AND
json_extract(record_json, '$.model') = model AND
json_extract(record_json, '$.policyRevision') = policy_revision AND
json_extract(record_json, '$.requestDigest') = request_digest AND
json_extract(record_json, '$.inputBytes') = input_bytes AND
json_extract(record_json, '$.maxOutputTokens') = max_output_tokens AND
json_extract(record_json, '$.deadlineAtMs') = deadline_at_ms AND
json_extract(record_json, '$.admittedAtMs') = admitted_at_ms AND
json_extract(record_json, '$.stepRunMutationId') = mutation_id AND
json_extract(record_json, '$.stepRunMutationDigest') = mutation_digest AND
json_extract(record_json, '$.runEventId') = run_event_id AND
json_extract(record_json, '$.startDigest') = start_digest
)
)`;
const LOCAL_COMPLETION_TABLE_SQL = `
CREATE TABLE "ModelInvocationCompletions" (
invocation_id TEXT PRIMARY KEY NOT NULL,
project_id TEXT NOT NULL,
run_id TEXT NOT NULL,
step_run_id TEXT NOT NULL,
trace_id TEXT NOT NULL,
start_digest TEXT NOT NULL,
outcome TEXT NOT NULL,
output_bytes INTEGER NOT NULL,
error_code TEXT,
completed_at_ms INTEGER NOT NULL,
mutation_id TEXT NOT NULL,
mutation_digest TEXT NOT NULL,
run_event_id TEXT NOT NULL,
completion_digest TEXT NOT NULL,
record_json TEXT NOT NULL,
FOREIGN KEY (invocation_id)
REFERENCES "ModelInvocationStarts" (invocation_id) ON DELETE RESTRICT,
FOREIGN KEY (run_id, step_run_id)
REFERENCES "StepRuns" (run_id, id) ON DELETE RESTRICT,
FOREIGN KEY (mutation_id)
REFERENCES "StepRunMutations" (mutation_id) ON DELETE RESTRICT,
FOREIGN KEY (run_event_id)
REFERENCES "RunEvents" (id) ON DELETE RESTRICT,
CONSTRAINT ql3_model_invocation_completions_outcome_check CHECK (
outcome IN ('succeeded', 'failed', 'timed_out', 'outcome_unknown')
),
CONSTRAINT ql3_model_invocation_completions_value_check CHECK (
output_bytes BETWEEN 0 AND 1048576 AND completed_at_ms >= 0 AND
((outcome = 'succeeded' AND error_code IS NULL) OR
(outcome <> 'succeeded' AND
length(error_code) BETWEEN 1 AND 64 AND
substr(error_code, 1, 1) GLOB '[A-Z]' AND
error_code NOT GLOB '*[^A-Z0-9_]*')) AND
length(start_digest) = 64 AND
start_digest NOT GLOB '*[^0-9a-f]*' AND
length(mutation_digest) = 64 AND
mutation_digest NOT GLOB '*[^0-9a-f]*' AND
length(completion_digest) = 64 AND
completion_digest NOT GLOB '*[^0-9a-f]*'
),
CONSTRAINT ql3_model_invocation_completions_json_check CHECK (
length(CAST(record_json AS BLOB)) BETWEEN 2 AND 24576 AND
json_valid(record_json) AND json_type(record_json) = 'object' AND
json_extract(record_json, '$.schema') =
'qinglong/model-invocation-completion@v1' AND
json_extract(record_json, '$.invocationId') = invocation_id AND
json_extract(record_json, '$.projectId') = project_id AND
json_extract(record_json, '$.runId') = run_id AND
json_extract(record_json, '$.stepRunId') = step_run_id AND
json_extract(record_json, '$.traceId') = trace_id AND
json_extract(record_json, '$.startDigest') = start_digest AND
json_extract(record_json, '$.outcome') = outcome AND
json_extract(record_json, '$.outputBytes') = output_bytes AND
json_extract(record_json, '$.errorCode') IS error_code AND
json_extract(record_json, '$.completedAtMs') = completed_at_ms AND
json_extract(record_json, '$.stepRunMutationId') = mutation_id AND
json_extract(record_json, '$.stepRunMutationDigest') = mutation_digest AND
json_extract(record_json, '$.runEventId') = run_event_id AND
json_extract(record_json, '$.completionDigest') = completion_digest
)
)`;
const LOCAL_RESOLUTION_TABLE_SQL = `
CREATE TABLE "ModelInvocationResolutions" (
resolution_id TEXT PRIMARY KEY NOT NULL,
invocation_id TEXT NOT NULL UNIQUE,
project_id TEXT NOT NULL,
run_id TEXT NOT NULL,
step_run_id TEXT NOT NULL,
trace_id TEXT NOT NULL,
completion_digest TEXT NOT NULL,
decision TEXT NOT NULL,
resolved_by_user_id TEXT NOT NULL,
resolved_at_ms INTEGER NOT NULL,
mutation_id TEXT NOT NULL UNIQUE,
mutation_digest TEXT NOT NULL,
run_event_id TEXT NOT NULL UNIQUE,
resolution_digest TEXT NOT NULL UNIQUE,
record_json TEXT NOT NULL,
FOREIGN KEY (invocation_id)
REFERENCES "ModelInvocationCompletions" (invocation_id) ON DELETE RESTRICT,
FOREIGN KEY (run_id, step_run_id)
REFERENCES "StepRuns" (run_id, id) ON DELETE RESTRICT,
FOREIGN KEY (mutation_id)
REFERENCES "StepRunMutations" (mutation_id) ON DELETE RESTRICT,
FOREIGN KEY (run_event_id)
REFERENCES "RunEvents" (id) ON DELETE RESTRICT,
CONSTRAINT ql3_model_invocation_resolutions_value_check CHECK (
decision IN ('retry', 'fail', 'cancel') AND
resolved_at_ms >= 0 AND
length(resolution_id) BETWEEN 1 AND 128 AND
length(invocation_id) BETWEEN 1 AND 128 AND
length(project_id) BETWEEN 1 AND 128 AND
length(run_id) BETWEEN 1 AND 128 AND
length(step_run_id) BETWEEN 1 AND 128 AND
length(trace_id) BETWEEN 1 AND 128 AND
length(resolved_by_user_id) BETWEEN 1 AND 128 AND
length(mutation_id) BETWEEN 1 AND 128 AND
length(run_event_id) BETWEEN 1 AND 36 AND
length(completion_digest) = 64 AND
completion_digest NOT GLOB '*[^0-9a-f]*' AND
length(mutation_digest) = 64 AND
mutation_digest NOT GLOB '*[^0-9a-f]*' AND
length(resolution_digest) = 64 AND
resolution_digest NOT GLOB '*[^0-9a-f]*'
),
CONSTRAINT ql3_model_invocation_resolutions_json_check CHECK (
length(CAST(record_json AS BLOB)) BETWEEN 2 AND 24576 AND
json_valid(record_json) AND json_type(record_json) = 'object' AND
json_extract(record_json, '$.schema') =
'qinglong/model-invocation-resolution@v1' AND
json_extract(record_json, '$.resolutionId') = resolution_id AND
json_extract(record_json, '$.invocationId') = invocation_id AND
json_extract(record_json, '$.projectId') = project_id AND
json_extract(record_json, '$.runId') = run_id AND
json_extract(record_json, '$.stepRunId') = step_run_id AND
json_extract(record_json, '$.traceId') = trace_id AND
json_extract(record_json, '$.completionDigest') = completion_digest AND
json_extract(record_json, '$.decision') = decision AND
json_extract(record_json, '$.resolvedByUserId') = resolved_by_user_id AND
json_extract(record_json, '$.resolvedAtMs') = resolved_at_ms AND
json_extract(record_json, '$.stepRunMutationId') = mutation_id AND
json_extract(record_json, '$.stepRunMutationDigest') = mutation_digest AND
json_extract(record_json, '$.runEventId') = run_event_id AND
json_extract(record_json, '$.resolutionDigest') = resolution_digest
)
)`;
const localMigration = defineSqlMigration<LocalMigrationContext>(
LOCAL_MODEL_INVOCATION_MIGRATION_ID,
[
LOCAL_START_TABLE_SQL,
`CREATE INDEX ql3_model_invocation_starts_step_history_idx
ON "ModelInvocationStarts"
(run_id, step_run_id, admitted_at_ms, invocation_id)`,
`CREATE UNIQUE INDEX ql3_model_invocation_starts_mutation_uidx
ON "ModelInvocationStarts" (mutation_id)`,
`CREATE UNIQUE INDEX ql3_model_invocation_starts_event_uidx
ON "ModelInvocationStarts" (run_event_id)`,
`CREATE UNIQUE INDEX ql3_model_invocation_starts_digest_uidx
ON "ModelInvocationStarts" (start_digest)`,
LOCAL_COMPLETION_TABLE_SQL,
`CREATE INDEX ql3_model_invocation_completions_step_history_idx
ON "ModelInvocationCompletions"
(run_id, step_run_id, completed_at_ms, invocation_id)`,
`CREATE UNIQUE INDEX ql3_model_invocation_completions_mutation_uidx
ON "ModelInvocationCompletions" (mutation_id)`,
`CREATE UNIQUE INDEX ql3_model_invocation_completions_event_uidx
ON "ModelInvocationCompletions" (run_event_id)`,
`CREATE UNIQUE INDEX ql3_model_invocation_completions_digest_uidx
ON "ModelInvocationCompletions" (completion_digest)`,
LOCAL_RESOLUTION_TABLE_SQL,
`CREATE INDEX ql3_model_invocation_resolutions_step_history_idx
ON "ModelInvocationResolutions"
(run_id, step_run_id, resolved_at_ms, resolution_id)`,
],
(context, statement) => context.client.exec(statement),
);
export const sqliteCoreMigrations = Object.freeze([
localMigration,
]);
@@ -0,0 +1,171 @@
import type { LocalMigrationContext } from './context';
import {
LOCAL_MODEL_PROVIDER_CREDENTIAL_CATALOG_MIGRATION_ID,
} from '../identities';
import { defineSqlMigration } from '../shared';
const LOCAL_MODEL_PROVIDER_CREDENTIAL_BINDING_TABLE_SQL = `
CREATE TABLE "ModelInvocationProviderCredentialBindings" (
project_id TEXT NOT NULL,
provider TEXT NOT NULL,
revision TEXT NOT NULL,
binding_digest TEXT NOT NULL,
binding_json TEXT NOT NULL,
PRIMARY KEY (project_id, provider, revision),
CONSTRAINT ql3_ai_local_provider_binding_identity_check CHECK (
length(project_id) BETWEEN 1 AND 128 AND
length(provider) BETWEEN 1 AND 128 AND
length(revision) BETWEEN 1 AND 128 AND
project_id NOT GLOB '*[^A-Za-z0-9._:/-]*' AND
provider NOT GLOB '*[^A-Za-z0-9._:/-]*' AND
revision NOT GLOB '*[^A-Za-z0-9._:/-]*'
),
CONSTRAINT ql3_ai_local_provider_binding_digest_check CHECK (
length(binding_digest) = 64 AND
binding_digest NOT GLOB '*[^0-9a-f]*'
),
CONSTRAINT ql3_ai_local_provider_binding_json_check CHECK (
length(CAST(binding_json AS BLOB)) BETWEEN 2 AND 4096 AND
json_valid(binding_json) AND json_type(binding_json) = 'object' AND
json_extract(binding_json, '$.schema') =
'qinglong/model-provider-credential-binding@v1' AND
json_extract(binding_json, '$.projectId') = project_id AND
json_extract(binding_json, '$.provider') = provider AND
json_extract(binding_json, '$.revision') = revision AND
json_extract(binding_json, '$.scheme') = 'bearer'
)
)`;
const LOCAL_MODEL_PROVIDER_CREDENTIAL_TRANSITION_TABLE_SQL = `
CREATE TABLE "ModelInvocationProviderCredentialTransitions" (
mutation_id TEXT PRIMARY KEY NOT NULL,
project_id TEXT NOT NULL,
provider TEXT NOT NULL,
generation INTEGER NOT NULL,
action TEXT NOT NULL,
active_binding_revision TEXT,
active_binding_digest TEXT,
previous_transition_digest TEXT,
changed_by_type TEXT NOT NULL,
changed_by_id TEXT NOT NULL,
changed_at_ms INTEGER NOT NULL,
command_digest TEXT NOT NULL,
transition_digest TEXT NOT NULL,
command_json TEXT NOT NULL,
transition_json TEXT NOT NULL,
UNIQUE (project_id, provider, generation),
UNIQUE (transition_digest),
FOREIGN KEY (project_id, provider, active_binding_revision)
REFERENCES "ModelInvocationProviderCredentialBindings"
(project_id, provider, revision) ON DELETE RESTRICT,
CONSTRAINT ql3_ai_local_provider_transition_identity_check CHECK (
length(mutation_id) BETWEEN 1 AND 128 AND
length(project_id) BETWEEN 1 AND 128 AND
length(provider) BETWEEN 1 AND 128 AND
length(changed_by_id) BETWEEN 1 AND 128 AND
project_id NOT GLOB '*[^A-Za-z0-9._:/-]*' AND
provider NOT GLOB '*[^A-Za-z0-9._:/-]*'
),
CONSTRAINT ql3_ai_local_provider_transition_value_check CHECK (
generation BETWEEN 1 AND 2147483647 AND
action IN ('bind', 'revoke') AND
changed_by_type = 'user' AND
changed_at_ms >= 0 AND
((action = 'bind' AND active_binding_revision IS NOT NULL AND
active_binding_digest IS NOT NULL) OR
(action = 'revoke' AND active_binding_revision IS NULL AND
active_binding_digest IS NULL))
),
CONSTRAINT ql3_ai_local_provider_transition_digest_check CHECK (
length(command_digest) = 64 AND command_digest NOT GLOB '*[^0-9a-f]*' AND
length(transition_digest) = 64 AND
transition_digest NOT GLOB '*[^0-9a-f]*' AND
(previous_transition_digest IS NULL OR
(length(previous_transition_digest) = 64 AND
previous_transition_digest NOT GLOB '*[^0-9a-f]*')) AND
(active_binding_digest IS NULL OR
(length(active_binding_digest) = 64 AND
active_binding_digest NOT GLOB '*[^0-9a-f]*'))
),
CONSTRAINT ql3_ai_local_provider_transition_json_check CHECK (
length(CAST(command_json AS BLOB)) BETWEEN 2 AND 8192 AND
json_valid(command_json) AND json_type(command_json) = 'object' AND
json_extract(command_json, '$.mutationId') = mutation_id AND
json_extract(command_json, '$.projectId') = project_id AND
json_extract(command_json, '$.provider') = provider AND
json_extract(command_json, '$.action') = action AND
length(CAST(transition_json AS BLOB)) BETWEEN 2 AND 8192 AND
json_valid(transition_json) AND json_type(transition_json) = 'object' AND
json_extract(transition_json, '$.schema') =
'qinglong/model-provider-credential-transition@v1' AND
json_extract(transition_json, '$.mutationId') = mutation_id AND
json_extract(transition_json, '$.projectId') = project_id AND
json_extract(transition_json, '$.provider') = provider AND
json_extract(transition_json, '$.generation') = generation AND
json_extract(transition_json, '$.action') = action AND
json_extract(transition_json, '$.transitionDigest') = transition_digest
)
)`;
const LOCAL_MODEL_PROVIDER_CREDENTIAL_AUDIT_TABLE_SQL = `
CREATE TABLE "ModelInvocationProviderCredentialAudits" (
operation TEXT NOT NULL,
project_id TEXT NOT NULL,
provider TEXT NOT NULL,
request_id TEXT NOT NULL,
binding_revision TEXT NOT NULL,
binding_digest TEXT NOT NULL,
occurred_at_ms INTEGER NOT NULL,
audit_json TEXT NOT NULL,
PRIMARY KEY (operation, project_id, provider, request_id),
CONSTRAINT ql3_ai_local_provider_audit_identity_check CHECK (
operation IN ('list_models', 'generate', 'stream') AND
length(project_id) BETWEEN 1 AND 128 AND
length(provider) BETWEEN 1 AND 128 AND
length(request_id) BETWEEN 1 AND 128 AND
length(binding_revision) BETWEEN 1 AND 128
),
CONSTRAINT ql3_ai_local_provider_audit_digest_check CHECK (
length(binding_digest) = 71 AND
binding_digest GLOB 'sha256:[0-9a-f]*' AND
occurred_at_ms >= 0
),
CONSTRAINT ql3_ai_local_provider_audit_json_check CHECK (
length(CAST(audit_json AS BLOB)) BETWEEN 2 AND 4096 AND
json_valid(audit_json) AND json_type(audit_json) = 'object' AND
json_extract(audit_json, '$.schema') =
'qinglong/model-provider-credential-audit@v1' AND
json_extract(audit_json, '$.operation') = operation AND
json_extract(audit_json, '$.projectId') = project_id AND
json_extract(audit_json, '$.provider') = provider AND
json_extract(audit_json, '$.requestId') = request_id AND
json_extract(audit_json, '$.bindingRevision') = binding_revision AND
json_extract(audit_json, '$.bindingDigest') = binding_digest AND
json_extract(audit_json, '$.occurredAtMs') = occurred_at_ms
)
)`;
const localModelProviderCredentialCatalogMigration =
defineSqlMigration<LocalMigrationContext>(
LOCAL_MODEL_PROVIDER_CREDENTIAL_CATALOG_MIGRATION_ID,
[
LOCAL_MODEL_PROVIDER_CREDENTIAL_BINDING_TABLE_SQL,
`CREATE UNIQUE INDEX ql3_ai_local_provider_binding_digest_uidx
ON "ModelInvocationProviderCredentialBindings" (binding_digest)`,
LOCAL_MODEL_PROVIDER_CREDENTIAL_TRANSITION_TABLE_SQL,
`CREATE INDEX ql3_ai_local_provider_transition_current_idx
ON "ModelInvocationProviderCredentialTransitions"
(project_id, provider, generation DESC)`,
LOCAL_MODEL_PROVIDER_CREDENTIAL_AUDIT_TABLE_SQL,
`CREATE INDEX ql3_ai_local_provider_audit_time_idx
ON "ModelInvocationProviderCredentialAudits"
(project_id, occurred_at_ms DESC, provider, request_id)`,
],
(context, statement) => context.client.exec(statement),
);
export const sqliteCredentialMigrations = Object.freeze([
localModelProviderCredentialCatalogMigration,
]);
@@ -0,0 +1,539 @@
import type { LocalMigrationContext } from './context';
import {
LOCAL_PLUGIN_PACKAGE_PROMPT_ADMISSION_MIGRATION_ID,
LOCAL_PLUGIN_PACKAGE_PROMPT_FINALIZATION_MIGRATION_ID,
LOCAL_PLUGIN_PACKAGE_PROMPT_OUTPUT_ARTIFACT_MIGRATION_ID,
LOCAL_PLUGIN_PACKAGE_PROMPT_OUTPUT_TOMBSTONE_MIGRATION_ID,
LOCAL_PLUGIN_PACKAGE_PROMPT_OUTPUT_KEY_RETIREMENT_MIGRATION_ID,
} from '../identities';
import { defineSqlMigration } from '../shared';
const LOCAL_PLUGIN_PACKAGE_PROMPT_ADMISSION_TABLE_SQL = `
CREATE TABLE "ModelInvocationPromptAdmissions" (
request_id TEXT PRIMARY KEY NOT NULL,
invocation_id TEXT NOT NULL UNIQUE,
plan_digest TEXT NOT NULL UNIQUE,
run_id TEXT NOT NULL UNIQUE,
step_run_id TEXT NOT NULL UNIQUE,
project_id TEXT NOT NULL,
package_name TEXT NOT NULL,
installation_id TEXT NOT NULL,
lock_digest TEXT NOT NULL,
generation INTEGER NOT NULL,
generation_digest TEXT NOT NULL,
materialized_revision_digest TEXT NOT NULL,
publication_digest TEXT NOT NULL,
prompt_id TEXT NOT NULL,
prompt_definition_digest TEXT NOT NULL,
parameter_digest TEXT NOT NULL,
model_request_digest TEXT NOT NULL,
admitted_at_ms INTEGER NOT NULL,
receipt_digest TEXT NOT NULL UNIQUE,
plan_json TEXT NOT NULL,
receipt_json TEXT NOT NULL,
CONSTRAINT ql3_ai_prompt_admission_run_fk
FOREIGN KEY (run_id) REFERENCES "Runs" (id)
ON DELETE RESTRICT ON UPDATE RESTRICT,
CONSTRAINT ql3_ai_prompt_admission_step_fk
FOREIGN KEY (run_id, step_run_id) REFERENCES "StepRuns" (run_id, id)
ON DELETE RESTRICT ON UPDATE RESTRICT,
CONSTRAINT ql3_ai_prompt_admission_publication_fk
FOREIGN KEY (publication_digest)
REFERENCES "QingLong3PluginPackageAutomationPublications" (
publication_digest
) ON DELETE RESTRICT ON UPDATE RESTRICT,
CONSTRAINT ql3_ai_prompt_admission_identity_check CHECK (
length(request_id) BETWEEN 1 AND 128 AND
length(invocation_id) BETWEEN 1 AND 128 AND
length(run_id) BETWEEN 1 AND 128 AND
length(step_run_id) BETWEEN 1 AND 128 AND
length(project_id) BETWEEN 1 AND 128 AND
length(package_name) BETWEEN 1 AND 63 AND
length(installation_id) BETWEEN 1 AND 128 AND
length(prompt_id) BETWEEN 1 AND 128 AND
generation BETWEEN 1 AND 2147483647 AND
admitted_at_ms >= 0
),
CONSTRAINT ql3_ai_prompt_admission_digest_check CHECK (
length(plan_digest) = 64 AND
plan_digest NOT GLOB '*[^0-9a-f]*' AND
length(lock_digest) = 64 AND
lock_digest NOT GLOB '*[^0-9a-f]*' AND
length(generation_digest) = 64 AND
generation_digest NOT GLOB '*[^0-9a-f]*' AND
length(materialized_revision_digest) = 64 AND
materialized_revision_digest NOT GLOB '*[^0-9a-f]*' AND
length(publication_digest) = 64 AND
publication_digest NOT GLOB '*[^0-9a-f]*' AND
length(prompt_definition_digest) = 64 AND
prompt_definition_digest NOT GLOB '*[^0-9a-f]*' AND
length(parameter_digest) = 64 AND
parameter_digest NOT GLOB '*[^0-9a-f]*' AND
model_request_digest GLOB 'sha256:[0-9a-f]*' AND
length(model_request_digest) = 71 AND
substr(model_request_digest, 8) NOT GLOB '*[^0-9a-f]*' AND
length(receipt_digest) = 64 AND
receipt_digest NOT GLOB '*[^0-9a-f]*'
),
CONSTRAINT ql3_ai_prompt_admission_plan_json_check CHECK (
length(CAST(plan_json AS BLOB)) BETWEEN 2 AND 32768 AND
json_valid(plan_json) AND json_type(plan_json) = 'object' AND
json_extract(plan_json, '$.schema') =
'qinglong/plugin-package-prompt-execution-plan@v1' AND
json_extract(plan_json, '$.requestId') = request_id AND
json_extract(plan_json, '$.invocationId') = invocation_id AND
json_extract(plan_json, '$.planDigest') = plan_digest AND
json_extract(plan_json, '$.runId') = run_id AND
json_extract(plan_json, '$.stepRunId') = step_run_id AND
json_extract(plan_json, '$.target.projectId') = project_id AND
json_extract(plan_json, '$.target.packageName') = package_name AND
json_extract(plan_json, '$.target.installationId') = installation_id AND
json_extract(plan_json, '$.target.lockDigest') = lock_digest AND
json_extract(plan_json, '$.target.generation') = generation AND
json_extract(plan_json, '$.target.generationDigest') =
generation_digest AND
json_extract(plan_json, '$.target.materializedRevisionDigest') =
materialized_revision_digest AND
json_extract(plan_json, '$.target.publicationDigest') =
publication_digest AND
json_extract(plan_json, '$.target.promptId') = prompt_id AND
json_extract(plan_json, '$.target.promptDefinitionDigest') =
prompt_definition_digest AND
json_extract(plan_json, '$.parameterDigest') = parameter_digest AND
json_extract(plan_json, '$.modelRequestDigest') = model_request_digest AND
json_extract(plan_json, '$.plannedAtMs') = admitted_at_ms
),
CONSTRAINT ql3_ai_prompt_admission_receipt_json_check CHECK (
length(CAST(receipt_json AS BLOB)) BETWEEN 2 AND 16384 AND
json_valid(receipt_json) AND json_type(receipt_json) = 'object' AND
json_extract(receipt_json, '$.schema') =
'qinglong/plugin-package-prompt-admission-receipt@v1' AND
json_extract(receipt_json, '$.requestId') = request_id AND
json_extract(receipt_json, '$.invocationId') = invocation_id AND
json_extract(receipt_json, '$.planDigest') = plan_digest AND
json_extract(receipt_json, '$.runId') = run_id AND
json_extract(receipt_json, '$.stepRunId') = step_run_id AND
json_extract(receipt_json, '$.publicationDigest') =
publication_digest AND
json_extract(receipt_json, '$.promptId') = prompt_id AND
json_extract(receipt_json, '$.admittedAtMs') = admitted_at_ms AND
json_extract(receipt_json, '$.receiptDigest') = receipt_digest
)
)`;
const localPluginPackagePromptAdmissionMigration =
defineSqlMigration<LocalMigrationContext>(
LOCAL_PLUGIN_PACKAGE_PROMPT_ADMISSION_MIGRATION_ID,
[
LOCAL_PLUGIN_PACKAGE_PROMPT_ADMISSION_TABLE_SQL,
`CREATE UNIQUE INDEX ql3_ai_prompt_admission_run_step_uidx
ON "ModelInvocationPromptAdmissions" (run_id, step_run_id)`,
`CREATE INDEX ql3_ai_prompt_admission_target_idx
ON "ModelInvocationPromptAdmissions"
(project_id, package_name, admitted_at_ms, request_id)`,
],
(context, statement) => context.client.exec(statement),
);
const LOCAL_PLUGIN_PACKAGE_PROMPT_FINALIZATION_TABLE_SQL = `
CREATE TABLE "ModelInvocationPromptFinalizations" (
request_id TEXT PRIMARY KEY NOT NULL,
invocation_id TEXT NOT NULL UNIQUE,
plan_digest TEXT NOT NULL UNIQUE,
run_id TEXT NOT NULL UNIQUE,
step_run_id TEXT NOT NULL UNIQUE,
terminal_evidence_kind TEXT NOT NULL,
terminal_evidence_digest TEXT NOT NULL UNIQUE,
final_step_run_digest TEXT NOT NULL,
run_status TEXT NOT NULL,
event_id TEXT NOT NULL UNIQUE,
final_run_version INTEGER NOT NULL,
final_run_event_sequence INTEGER NOT NULL,
finalized_at_ms INTEGER NOT NULL,
receipt_digest TEXT NOT NULL UNIQUE,
receipt_json TEXT NOT NULL,
CONSTRAINT ql3_ai_prompt_finalization_admission_fk
FOREIGN KEY (request_id) REFERENCES "ModelInvocationPromptAdmissions" (
request_id
) ON DELETE RESTRICT ON UPDATE RESTRICT,
CONSTRAINT ql3_ai_prompt_finalization_invocation_fk
FOREIGN KEY (invocation_id) REFERENCES "ModelInvocationPromptAdmissions" (
invocation_id
) ON DELETE RESTRICT ON UPDATE RESTRICT,
CONSTRAINT ql3_ai_prompt_finalization_plan_fk
FOREIGN KEY (plan_digest) REFERENCES "ModelInvocationPromptAdmissions" (
plan_digest
) ON DELETE RESTRICT ON UPDATE RESTRICT,
CONSTRAINT ql3_ai_prompt_finalization_run_fk
FOREIGN KEY (run_id) REFERENCES "Runs" (id)
ON DELETE RESTRICT ON UPDATE RESTRICT,
CONSTRAINT ql3_ai_prompt_finalization_step_fk
FOREIGN KEY (run_id, step_run_id) REFERENCES "StepRuns" (run_id, id)
ON DELETE RESTRICT ON UPDATE RESTRICT,
CONSTRAINT ql3_ai_prompt_finalization_event_fk
FOREIGN KEY (event_id) REFERENCES "RunEvents" (id)
ON DELETE RESTRICT ON UPDATE RESTRICT,
CONSTRAINT ql3_ai_prompt_finalization_identity_check CHECK (
length(request_id) BETWEEN 1 AND 128 AND
length(invocation_id) BETWEEN 1 AND 128 AND
length(run_id) BETWEEN 1 AND 128 AND
length(step_run_id) BETWEEN 1 AND 128 AND
length(event_id) BETWEEN 1 AND 128 AND
terminal_evidence_kind IN ('completion', 'resolution') AND
run_status IN ('succeeded', 'failed', 'cancelled', 'timed_out') AND
final_run_version BETWEEN 3 AND 2147483647 AND
final_run_event_sequence = final_run_version AND
finalized_at_ms >= 0
),
CONSTRAINT ql3_ai_prompt_finalization_digest_check CHECK (
length(plan_digest) = 64 AND
plan_digest NOT GLOB '*[^0-9a-f]*' AND
length(terminal_evidence_digest) = 64 AND
terminal_evidence_digest NOT GLOB '*[^0-9a-f]*' AND
length(final_step_run_digest) = 64 AND
final_step_run_digest NOT GLOB '*[^0-9a-f]*' AND
length(receipt_digest) = 64 AND
receipt_digest NOT GLOB '*[^0-9a-f]*'
),
CONSTRAINT ql3_ai_prompt_finalization_receipt_json_check CHECK (
length(CAST(receipt_json AS BLOB)) BETWEEN 2 AND 16384 AND
json_valid(receipt_json) AND json_type(receipt_json) = 'object' AND
json_extract(receipt_json, '$.schema') =
'qinglong/plugin-package-prompt-finalization-receipt@v1' AND
json_extract(receipt_json, '$.requestId') = request_id AND
json_extract(receipt_json, '$.invocationId') = invocation_id AND
json_extract(receipt_json, '$.planDigest') = plan_digest AND
json_extract(receipt_json, '$.runId') = run_id AND
json_extract(receipt_json, '$.stepRunId') = step_run_id AND
json_extract(receipt_json, '$.terminalEvidenceKind') =
terminal_evidence_kind AND
json_extract(receipt_json, '$.terminalEvidenceDigest') =
terminal_evidence_digest AND
json_extract(receipt_json, '$.finalStepRunDigest') =
final_step_run_digest AND
json_extract(receipt_json, '$.runStatus') = run_status AND
json_extract(receipt_json, '$.eventId') = event_id AND
json_extract(receipt_json, '$.finalRunVersion') = final_run_version AND
json_extract(receipt_json, '$.finalRunEventSequence') =
final_run_event_sequence AND
json_extract(receipt_json, '$.finalizedAtMs') = finalized_at_ms AND
json_extract(receipt_json, '$.receiptDigest') = receipt_digest
)
)`;
const localPluginPackagePromptFinalizationMigration =
defineSqlMigration<LocalMigrationContext>(
LOCAL_PLUGIN_PACKAGE_PROMPT_FINALIZATION_MIGRATION_ID,
[
LOCAL_PLUGIN_PACKAGE_PROMPT_FINALIZATION_TABLE_SQL,
`CREATE UNIQUE INDEX ql3_ai_prompt_finalization_run_step_uidx
ON "ModelInvocationPromptFinalizations" (run_id, step_run_id)`,
`CREATE INDEX ql3_ai_prompt_finalization_status_idx
ON "ModelInvocationPromptFinalizations"
(run_status, finalized_at_ms, request_id)`,
],
(context, statement) => context.client.exec(statement),
);
const LOCAL_PLUGIN_PACKAGE_PROMPT_OUTPUT_ARTIFACT_TABLE_SQL = `
CREATE TABLE "ModelInvocationPromptOutputArtifacts" (
artifact_id TEXT PRIMARY KEY NOT NULL,
project_id TEXT NOT NULL,
run_id TEXT NOT NULL,
step_run_id TEXT NOT NULL,
invocation_id TEXT NOT NULL UNIQUE,
requested_by_type TEXT NOT NULL,
requested_by_id TEXT NOT NULL,
provider TEXT NOT NULL,
model TEXT NOT NULL,
content_digest TEXT NOT NULL,
output_bytes INTEGER NOT NULL,
retention_policy_revision TEXT NOT NULL,
retention_ms INTEGER NOT NULL,
retention_policy_digest TEXT NOT NULL,
retention_eligible_at_ms INTEGER NOT NULL,
key_id TEXT NOT NULL,
algorithm TEXT NOT NULL,
plaintext_bytes INTEGER NOT NULL,
sealed_at_ms INTEGER NOT NULL,
artifact_digest TEXT NOT NULL UNIQUE,
artifact_json TEXT NOT NULL,
CONSTRAINT ql3_ai_prompt_output_artifact_admission_fk
FOREIGN KEY (invocation_id)
REFERENCES "ModelInvocationPromptAdmissions" (invocation_id)
ON DELETE RESTRICT ON UPDATE RESTRICT,
CONSTRAINT ql3_ai_prompt_output_artifact_start_fk
FOREIGN KEY (invocation_id)
REFERENCES "ModelInvocationStarts" (invocation_id)
ON DELETE RESTRICT ON UPDATE RESTRICT,
CONSTRAINT ql3_ai_prompt_output_artifact_step_fk
FOREIGN KEY (run_id, step_run_id) REFERENCES "StepRuns" (run_id, id)
ON DELETE RESTRICT ON UPDATE RESTRICT,
CONSTRAINT ql3_ai_prompt_output_artifact_identity_check CHECK (
length(artifact_id) BETWEEN 1 AND 128 AND
length(project_id) BETWEEN 1 AND 128 AND
length(run_id) BETWEEN 1 AND 128 AND
length(step_run_id) BETWEEN 1 AND 128 AND
length(invocation_id) BETWEEN 1 AND 128 AND
length(requested_by_type) BETWEEN 1 AND 32 AND
length(requested_by_id) BETWEEN 1 AND 128 AND
length(provider) BETWEEN 1 AND 256 AND
length(model) BETWEEN 1 AND 256 AND
length(retention_policy_revision) BETWEEN 1 AND 128 AND
length(key_id) BETWEEN 1 AND 128 AND
algorithm = 'aes-256-gcm'
),
CONSTRAINT ql3_ai_prompt_output_artifact_value_check CHECK (
output_bytes BETWEEN 0 AND 1048576 AND
plaintext_bytes BETWEEN 1 AND 1052672 AND
retention_ms BETWEEN 3600000 AND 31536000000 AND
sealed_at_ms >= 0 AND
retention_eligible_at_ms = sealed_at_ms + retention_ms AND
length(content_digest) = 64 AND
content_digest NOT GLOB '*[^0-9a-f]*' AND
length(retention_policy_digest) = 64 AND
retention_policy_digest NOT GLOB '*[^0-9a-f]*' AND
length(artifact_digest) = 64 AND
artifact_digest NOT GLOB '*[^0-9a-f]*'
),
CONSTRAINT ql3_ai_prompt_output_artifact_json_check CHECK (
length(CAST(artifact_json AS BLOB)) BETWEEN 2 AND 1572864 AND
json_valid(artifact_json) AND json_type(artifact_json) = 'object' AND
json_extract(artifact_json, '$.schema') =
'qinglong/plugin-package-prompt-output-artifact@v1' AND
json_extract(artifact_json, '$.artifactId') = artifact_id AND
json_extract(artifact_json, '$.projectId') = project_id AND
json_extract(artifact_json, '$.runId') = run_id AND
json_extract(artifact_json, '$.stepRunId') = step_run_id AND
json_extract(artifact_json, '$.invocationId') = invocation_id AND
json_extract(artifact_json, '$.requestedBy.type') = requested_by_type AND
json_extract(artifact_json, '$.requestedBy.id') = requested_by_id AND
json_extract(artifact_json, '$.provider') = provider AND
json_extract(artifact_json, '$.model') = model AND
json_extract(artifact_json, '$.contentDigest') = content_digest AND
json_extract(artifact_json, '$.outputBytes') = output_bytes AND
json_extract(artifact_json, '$.retentionPolicy.revision') =
retention_policy_revision AND
json_extract(artifact_json, '$.retentionPolicy.retentionMs') =
retention_ms AND
json_extract(artifact_json, '$.retentionPolicyDigest') =
retention_policy_digest AND
json_extract(artifact_json, '$.retentionEligibleAtMs') =
retention_eligible_at_ms AND
json_extract(artifact_json, '$.keyId') = key_id AND
json_extract(artifact_json, '$.algorithm') = algorithm AND
json_extract(artifact_json, '$.plaintextBytes') = plaintext_bytes AND
json_extract(artifact_json, '$.sealedAtMs') = sealed_at_ms AND
json_extract(artifact_json, '$.artifactDigest') = artifact_digest
)
)`;
const localPluginPackagePromptOutputArtifactMigration =
defineSqlMigration<LocalMigrationContext>(
LOCAL_PLUGIN_PACKAGE_PROMPT_OUTPUT_ARTIFACT_MIGRATION_ID,
[
LOCAL_PLUGIN_PACKAGE_PROMPT_OUTPUT_ARTIFACT_TABLE_SQL,
`CREATE INDEX ql3_ai_prompt_output_artifact_retention_idx
ON "ModelInvocationPromptOutputArtifacts"
(retention_eligible_at_ms, artifact_id)`,
`CREATE INDEX ql3_ai_prompt_output_artifact_project_run_idx
ON "ModelInvocationPromptOutputArtifacts"
(project_id, run_id, artifact_id)`,
],
(context, statement) => context.client.exec(statement),
);
const LOCAL_PLUGIN_PACKAGE_PROMPT_OUTPUT_TOMBSTONE_TABLE_SQL = `
CREATE TABLE "ModelInvocationPromptOutputArtifactTombstones" (
artifact_id TEXT PRIMARY KEY NOT NULL,
project_id TEXT NOT NULL,
run_id TEXT NOT NULL,
step_run_id TEXT NOT NULL,
invocation_id TEXT NOT NULL UNIQUE,
artifact_digest TEXT NOT NULL UNIQUE,
retention_policy_digest TEXT NOT NULL,
retention_eligible_at_ms INTEGER NOT NULL,
key_id TEXT NOT NULL,
tombstoned_at_ms INTEGER NOT NULL,
tombstone_digest TEXT NOT NULL UNIQUE,
tombstone_json TEXT NOT NULL,
CONSTRAINT ql3_ai_prompt_output_tombstone_admission_fk
FOREIGN KEY (invocation_id)
REFERENCES "ModelInvocationPromptAdmissions" (invocation_id)
ON DELETE RESTRICT ON UPDATE RESTRICT,
CONSTRAINT ql3_ai_prompt_output_tombstone_start_fk
FOREIGN KEY (invocation_id)
REFERENCES "ModelInvocationStarts" (invocation_id)
ON DELETE RESTRICT ON UPDATE RESTRICT,
CONSTRAINT ql3_ai_prompt_output_tombstone_step_fk
FOREIGN KEY (run_id, step_run_id) REFERENCES "StepRuns" (run_id, id)
ON DELETE RESTRICT ON UPDATE RESTRICT,
CONSTRAINT ql3_ai_prompt_output_tombstone_identity_check CHECK (
length(artifact_id) BETWEEN 1 AND 128 AND
length(project_id) BETWEEN 1 AND 128 AND
length(run_id) BETWEEN 1 AND 128 AND
length(step_run_id) BETWEEN 1 AND 128 AND
length(invocation_id) BETWEEN 1 AND 128 AND
length(key_id) BETWEEN 1 AND 128
),
CONSTRAINT ql3_ai_prompt_output_tombstone_value_check CHECK (
retention_eligible_at_ms >= 0 AND
tombstoned_at_ms >= retention_eligible_at_ms AND
length(artifact_digest) = 64 AND
artifact_digest NOT GLOB '*[^0-9a-f]*' AND
length(retention_policy_digest) = 64 AND
retention_policy_digest NOT GLOB '*[^0-9a-f]*' AND
length(tombstone_digest) = 64 AND
tombstone_digest NOT GLOB '*[^0-9a-f]*'
),
CONSTRAINT ql3_ai_prompt_output_tombstone_json_check CHECK (
length(CAST(tombstone_json AS BLOB)) BETWEEN 2 AND 8192 AND
json_valid(tombstone_json) AND json_type(tombstone_json) = 'object' AND
json_extract(tombstone_json, '$.schema') =
'qinglong/plugin-package-prompt-output-artifact-tombstone@v1' AND
json_extract(tombstone_json, '$.reference.artifactId') = artifact_id AND
json_extract(tombstone_json, '$.reference.projectId') = project_id AND
json_extract(tombstone_json, '$.reference.runId') = run_id AND
json_extract(tombstone_json, '$.reference.stepRunId') = step_run_id AND
json_extract(tombstone_json, '$.reference.invocationId') = invocation_id AND
json_extract(tombstone_json, '$.reference.artifactDigest') = artifact_digest AND
json_extract(tombstone_json, '$.reference.retentionPolicyDigest') =
retention_policy_digest AND
json_extract(tombstone_json, '$.reference.retentionEligibleAtMs') =
retention_eligible_at_ms AND
json_extract(tombstone_json, '$.reference.keyId') = key_id AND
json_extract(tombstone_json, '$.tombstonedAtMs') = tombstoned_at_ms AND
json_extract(tombstone_json, '$.tombstoneDigest') = tombstone_digest
)
)`;
const localPluginPackagePromptOutputTombstoneMigration =
defineSqlMigration<LocalMigrationContext>(
LOCAL_PLUGIN_PACKAGE_PROMPT_OUTPUT_TOMBSTONE_MIGRATION_ID,
[
LOCAL_PLUGIN_PACKAGE_PROMPT_OUTPUT_TOMBSTONE_TABLE_SQL,
`CREATE INDEX ql3_ai_prompt_output_tombstone_time_idx
ON "ModelInvocationPromptOutputArtifactTombstones"
(tombstoned_at_ms, artifact_id)`,
`CREATE INDEX ql3_ai_prompt_output_tombstone_project_run_idx
ON "ModelInvocationPromptOutputArtifactTombstones"
(project_id, run_id, artifact_id)`,
],
(context, statement) => context.client.exec(statement),
);
const LOCAL_PLUGIN_PACKAGE_PROMPT_OUTPUT_KEY_RETIREMENT_PREPARATION_TABLE_SQL = `
CREATE TABLE "ModelInvocationPromptOutputKeyRetirementPreparations" (
key_id TEXT PRIMARY KEY NOT NULL,
retirement_id TEXT NOT NULL UNIQUE,
request_id TEXT NOT NULL UNIQUE,
mutation_id TEXT NOT NULL UNIQUE,
catalog_digest TEXT NOT NULL,
material_proof TEXT NOT NULL,
prepared_at_ms INTEGER NOT NULL,
preparation_digest TEXT NOT NULL UNIQUE,
preparation_json TEXT NOT NULL,
CONSTRAINT ql3_ai_prompt_output_key_retirement_preparation_identity_check CHECK (
length(key_id) BETWEEN 1 AND 128 AND
length(retirement_id) BETWEEN 1 AND 128 AND
length(request_id) BETWEEN 1 AND 128 AND
length(mutation_id) BETWEEN 1 AND 128
),
CONSTRAINT ql3_ai_prompt_output_key_retirement_preparation_value_check CHECK (
prepared_at_ms >= 0 AND
length(catalog_digest) = 64 AND catalog_digest NOT GLOB '*[^0-9a-f]*' AND
length(material_proof) = 64 AND material_proof NOT GLOB '*[^0-9a-f]*' AND
length(preparation_digest) = 64 AND
preparation_digest NOT GLOB '*[^0-9a-f]*'
),
CONSTRAINT ql3_ai_prompt_output_key_retirement_preparation_json_check CHECK (
length(CAST(preparation_json AS BLOB)) BETWEEN 2 AND 8192 AND
json_valid(preparation_json) AND json_type(preparation_json) = 'object' AND
json_extract(preparation_json, '$.schema') =
'qinglong/plugin-package-prompt-output-key-retirement-preparation@v1' AND
json_extract(preparation_json, '$.keyId') = key_id AND
json_extract(preparation_json, '$.retirementId') = retirement_id AND
json_extract(preparation_json, '$.requestId') = request_id AND
json_extract(preparation_json, '$.mutationId') = mutation_id AND
json_extract(preparation_json, '$.catalogDigest') = catalog_digest AND
json_extract(preparation_json, '$.materialProof') = material_proof AND
json_extract(preparation_json, '$.preparedAtMs') = prepared_at_ms AND
json_extract(preparation_json, '$.preparationDigest') = preparation_digest
)
)`;
const LOCAL_PLUGIN_PACKAGE_PROMPT_OUTPUT_KEY_RETIREMENT_COMPLETION_TABLE_SQL = `
CREATE TABLE "ModelInvocationPromptOutputKeyRetirementCompletions" (
key_id TEXT PRIMARY KEY NOT NULL,
retirement_id TEXT NOT NULL UNIQUE,
request_id TEXT NOT NULL UNIQUE,
mutation_id TEXT NOT NULL UNIQUE,
preparation_digest TEXT NOT NULL UNIQUE,
retired_catalog_digest TEXT NOT NULL,
absence_proof TEXT NOT NULL,
completed_at_ms INTEGER NOT NULL,
completion_digest TEXT NOT NULL UNIQUE,
completion_json TEXT NOT NULL,
CONSTRAINT ql3_ai_prompt_output_key_retirement_completion_preparation_fk
FOREIGN KEY (key_id, preparation_digest)
REFERENCES "ModelInvocationPromptOutputKeyRetirementPreparations"
(key_id, preparation_digest)
ON DELETE RESTRICT ON UPDATE RESTRICT,
CONSTRAINT ql3_ai_prompt_output_key_retirement_completion_identity_check CHECK (
length(key_id) BETWEEN 1 AND 128 AND
length(retirement_id) BETWEEN 1 AND 128 AND
length(request_id) BETWEEN 1 AND 128 AND
length(mutation_id) BETWEEN 1 AND 128
),
CONSTRAINT ql3_ai_prompt_output_key_retirement_completion_value_check CHECK (
completed_at_ms >= 0 AND
length(preparation_digest) = 64 AND
preparation_digest NOT GLOB '*[^0-9a-f]*' AND
length(retired_catalog_digest) = 64 AND
retired_catalog_digest NOT GLOB '*[^0-9a-f]*' AND
length(absence_proof) = 64 AND absence_proof NOT GLOB '*[^0-9a-f]*' AND
length(completion_digest) = 64 AND completion_digest NOT GLOB '*[^0-9a-f]*'
),
CONSTRAINT ql3_ai_prompt_output_key_retirement_completion_json_check CHECK (
length(CAST(completion_json AS BLOB)) BETWEEN 2 AND 8192 AND
json_valid(completion_json) AND json_type(completion_json) = 'object' AND
json_extract(completion_json, '$.schema') =
'qinglong/plugin-package-prompt-output-key-retirement-completion@v1' AND
json_extract(completion_json, '$.keyId') = key_id AND
json_extract(completion_json, '$.retirementId') = retirement_id AND
json_extract(completion_json, '$.requestId') = request_id AND
json_extract(completion_json, '$.mutationId') = mutation_id AND
json_extract(completion_json, '$.preparationDigest') = preparation_digest AND
json_extract(completion_json, '$.retiredCatalogDigest') =
retired_catalog_digest AND
json_extract(completion_json, '$.absenceProof') = absence_proof AND
json_extract(completion_json, '$.completedAtMs') = completed_at_ms AND
json_extract(completion_json, '$.completionDigest') = completion_digest
)
)`;
const localPluginPackagePromptOutputKeyRetirementMigration =
defineSqlMigration<LocalMigrationContext>(
LOCAL_PLUGIN_PACKAGE_PROMPT_OUTPUT_KEY_RETIREMENT_MIGRATION_ID,
[
LOCAL_PLUGIN_PACKAGE_PROMPT_OUTPUT_KEY_RETIREMENT_PREPARATION_TABLE_SQL,
`CREATE UNIQUE INDEX ql3_ai_prompt_output_key_retirement_preparation_fk_uidx
ON "ModelInvocationPromptOutputKeyRetirementPreparations"
(key_id, preparation_digest)`,
LOCAL_PLUGIN_PACKAGE_PROMPT_OUTPUT_KEY_RETIREMENT_COMPLETION_TABLE_SQL,
`CREATE INDEX ql3_ai_prompt_output_key_retirement_completion_time_idx
ON "ModelInvocationPromptOutputKeyRetirementCompletions"
(completed_at_ms, key_id)`,
],
(context, statement) => context.client.exec(statement),
);
export const sqlitePromptMigrations = Object.freeze([
localPluginPackagePromptAdmissionMigration,
localPluginPackagePromptFinalizationMigration,
localPluginPackagePromptOutputArtifactMigration,
localPluginPackagePromptOutputTombstoneMigration,
localPluginPackagePromptOutputKeyRetirementMigration,
]);
@@ -0,0 +1,374 @@
import type { LocalMigrationContext } from './context';
import {
LOCAL_MODEL_INVOCATION_USAGE_MIGRATION_ID,
LOCAL_MODEL_INVOCATION_QUOTA_MIGRATION_ID,
LOCAL_MODEL_INVOCATION_PRICING_MIGRATION_ID,
} from '../identities';
import { defineSqlMigration } from '../shared';
const LOCAL_USAGE_LEDGER_TABLE_SQL = `
CREATE TABLE "ModelInvocationUsageLedger" (
invocation_id TEXT PRIMARY KEY NOT NULL,
project_id TEXT NOT NULL,
run_id TEXT NOT NULL,
step_run_id TEXT NOT NULL,
trace_id TEXT NOT NULL,
provider TEXT NOT NULL,
model TEXT NOT NULL,
policy_revision TEXT NOT NULL,
completion_digest TEXT NOT NULL,
outcome TEXT NOT NULL,
settled_at_ms INTEGER NOT NULL,
input_bytes INTEGER NOT NULL,
output_bytes INTEGER NOT NULL,
input_tokens INTEGER NOT NULL,
output_tokens INTEGER NOT NULL,
total_tokens INTEGER NOT NULL,
cost_micros INTEGER,
ledger_digest TEXT NOT NULL,
record_json TEXT NOT NULL,
FOREIGN KEY (invocation_id, completion_digest)
REFERENCES "ModelInvocationCompletions"
(invocation_id, completion_digest) ON DELETE RESTRICT,
CONSTRAINT ql3_model_invocation_usage_identity_check CHECK (
length(invocation_id) BETWEEN 1 AND 128 AND
length(project_id) BETWEEN 1 AND 128 AND
length(run_id) BETWEEN 1 AND 128 AND
length(step_run_id) BETWEEN 1 AND 128 AND
length(trace_id) BETWEEN 1 AND 128 AND
length(provider) BETWEEN 1 AND 128 AND
length(model) BETWEEN 1 AND 128 AND
length(policy_revision) BETWEEN 1 AND 128
),
CONSTRAINT ql3_model_invocation_usage_value_check CHECK (
outcome IN ('succeeded', 'failed', 'timed_out', 'outcome_unknown') AND
settled_at_ms >= 0 AND
input_bytes BETWEEN 1 AND 262144 AND
output_bytes BETWEEN 0 AND 1048576 AND
input_tokens >= 0 AND output_tokens >= 0 AND
total_tokens = input_tokens + output_tokens AND
(cost_micros IS NULL OR cost_micros >= 0) AND
length(completion_digest) = 64 AND
completion_digest NOT GLOB '*[^0-9a-f]*' AND
length(ledger_digest) = 64 AND
ledger_digest NOT GLOB '*[^0-9a-f]*'
),
CONSTRAINT ql3_model_invocation_usage_json_check CHECK (
length(CAST(record_json AS BLOB)) BETWEEN 2 AND 24576 AND
json_valid(record_json) AND json_type(record_json) = 'object' AND
json_extract(record_json, '$.schema') =
'qinglong/model-invocation-usage-ledger@v1' AND
json_extract(record_json, '$.invocationId') = invocation_id AND
json_extract(record_json, '$.projectId') = project_id AND
json_extract(record_json, '$.runId') = run_id AND
json_extract(record_json, '$.stepRunId') = step_run_id AND
json_extract(record_json, '$.traceId') = trace_id AND
json_extract(record_json, '$.provider') = provider AND
json_extract(record_json, '$.model') = model AND
json_extract(record_json, '$.policyRevision') = policy_revision AND
json_extract(record_json, '$.completionDigest') = completion_digest AND
json_extract(record_json, '$.outcome') = outcome AND
json_extract(record_json, '$.settledAtMs') = settled_at_ms AND
json_extract(record_json, '$.inputBytes') = input_bytes AND
json_extract(record_json, '$.outputBytes') = output_bytes AND
json_extract(record_json, '$.inputTokens') = input_tokens AND
json_extract(record_json, '$.outputTokens') = output_tokens AND
json_extract(record_json, '$.totalTokens') = total_tokens AND
((cost_micros IS NULL AND
json_type(record_json, '$.costMicros') = 'null') OR
json_extract(record_json, '$.costMicros') = cost_micros) AND
json_extract(record_json, '$.ledgerDigest') = ledger_digest
)
)`;
const localUsageMigration = defineSqlMigration<LocalMigrationContext>(
LOCAL_MODEL_INVOCATION_USAGE_MIGRATION_ID,
[
`CREATE UNIQUE INDEX ql3_model_invocation_completions_identity_digest_uidx
ON "ModelInvocationCompletions" (invocation_id, completion_digest)`,
LOCAL_USAGE_LEDGER_TABLE_SQL,
`CREATE INDEX ql3_model_invocation_usage_project_time_idx
ON "ModelInvocationUsageLedger"
(project_id, settled_at_ms, invocation_id)`,
`CREATE UNIQUE INDEX ql3_model_invocation_usage_digest_uidx
ON "ModelInvocationUsageLedger" (ledger_digest)`,
],
(context, statement) => context.client.exec(statement),
);
const LOCAL_QUOTA_RESERVATION_TABLE_SQL = `
CREATE TABLE "ModelInvocationQuotaReservations" (
invocation_id TEXT PRIMARY KEY NOT NULL,
project_id TEXT NOT NULL,
model_policy_revision TEXT NOT NULL,
quota_policy_revision TEXT NOT NULL,
window_ms INTEGER NOT NULL,
window_start_ms INTEGER NOT NULL,
window_end_ms INTEGER NOT NULL,
max_invocations INTEGER NOT NULL,
max_tokens INTEGER NOT NULL,
max_cost_micros INTEGER,
reserved_tokens INTEGER NOT NULL,
reserved_cost_micros INTEGER,
reserved_at_ms INTEGER NOT NULL,
admission_digest TEXT NOT NULL,
reservation_digest TEXT NOT NULL,
record_json TEXT NOT NULL,
FOREIGN KEY (invocation_id)
REFERENCES "ModelInvocationStarts" (invocation_id) ON DELETE RESTRICT,
CONSTRAINT ql3_model_invocation_quota_reservation_value_check CHECK (
window_ms IN (60000, 3600000, 86400000) AND
window_start_ms >= 0 AND window_end_ms = window_start_ms + window_ms AND
reserved_at_ms >= window_start_ms AND reserved_at_ms < window_end_ms AND
max_invocations BETWEEN 1 AND 100000 AND
max_tokens BETWEEN 1 AND 1000000000000 AND
(max_cost_micros IS NULL OR
max_cost_micros BETWEEN 0 AND 1000000000000000) AND
reserved_tokens BETWEEN 1 AND 1000000000000 AND
(reserved_cost_micros IS NULL OR
reserved_cost_micros BETWEEN 0 AND 1000000000000000) AND
(max_cost_micros IS NULL OR reserved_cost_micros IS NOT NULL) AND
length(admission_digest) = 64 AND
admission_digest NOT GLOB '*[^0-9a-f]*' AND
length(reservation_digest) = 64 AND
reservation_digest NOT GLOB '*[^0-9a-f]*'
),
CONSTRAINT ql3_model_invocation_quota_reservation_json_check CHECK (
length(CAST(record_json AS BLOB)) BETWEEN 2 AND 24576 AND
json_valid(record_json) AND json_type(record_json) = 'object' AND
json_extract(record_json, '$.schema') =
'qinglong/model-invocation-quota-reservation@v1' AND
json_extract(record_json, '$.invocationId') = invocation_id AND
json_extract(record_json, '$.projectId') = project_id AND
json_extract(record_json, '$.modelPolicyRevision') =
model_policy_revision AND
json_extract(record_json, '$.quotaPolicyRevision') =
quota_policy_revision AND
json_extract(record_json, '$.windowMs') = window_ms AND
json_extract(record_json, '$.windowStartMs') = window_start_ms AND
json_extract(record_json, '$.windowEndMs') = window_end_ms AND
json_extract(record_json, '$.maxInvocations') = max_invocations AND
json_extract(record_json, '$.maxTokens') = max_tokens AND
((max_cost_micros IS NULL AND
json_type(record_json, '$.maxCostMicros') = 'null') OR
json_extract(record_json, '$.maxCostMicros') = max_cost_micros) AND
json_extract(record_json, '$.reservedTokens') = reserved_tokens AND
((reserved_cost_micros IS NULL AND
json_type(record_json, '$.reservedCostMicros') = 'null') OR
json_extract(record_json, '$.reservedCostMicros') =
reserved_cost_micros) AND
json_extract(record_json, '$.reservedAtMs') = reserved_at_ms AND
json_extract(record_json, '$.admissionDigest') = admission_digest AND
json_extract(record_json, '$.reservationDigest') = reservation_digest
)
)`;
const LOCAL_QUOTA_SETTLEMENT_TABLE_SQL = `
CREATE TABLE "ModelInvocationQuotaSettlements" (
invocation_id TEXT PRIMARY KEY NOT NULL,
project_id TEXT NOT NULL,
reservation_digest TEXT NOT NULL,
completion_digest TEXT NOT NULL,
effective_tokens INTEGER NOT NULL,
effective_cost_micros INTEGER,
retained_token_reservation INTEGER NOT NULL,
retained_cost_reservation INTEGER NOT NULL,
settled_at_ms INTEGER NOT NULL,
settlement_digest TEXT NOT NULL,
record_json TEXT NOT NULL,
FOREIGN KEY (invocation_id)
REFERENCES "ModelInvocationQuotaReservations" (invocation_id)
ON DELETE RESTRICT,
FOREIGN KEY (invocation_id, completion_digest)
REFERENCES "ModelInvocationCompletions"
(invocation_id, completion_digest) ON DELETE RESTRICT,
CONSTRAINT ql3_model_invocation_quota_settlement_value_check CHECK (
effective_tokens BETWEEN 0 AND 1000000000000 AND
(effective_cost_micros IS NULL OR
effective_cost_micros BETWEEN 0 AND 1000000000000000) AND
retained_token_reservation IN (0, 1) AND
retained_cost_reservation IN (0, 1) AND
settled_at_ms >= 0 AND
length(reservation_digest) = 64 AND
reservation_digest NOT GLOB '*[^0-9a-f]*' AND
length(completion_digest) = 64 AND
completion_digest NOT GLOB '*[^0-9a-f]*' AND
length(settlement_digest) = 64 AND
settlement_digest NOT GLOB '*[^0-9a-f]*'
),
CONSTRAINT ql3_model_invocation_quota_settlement_json_check CHECK (
length(CAST(record_json AS BLOB)) BETWEEN 2 AND 24576 AND
json_valid(record_json) AND json_type(record_json) = 'object' AND
json_extract(record_json, '$.schema') =
'qinglong/model-invocation-quota-settlement@v1' AND
json_extract(record_json, '$.invocationId') = invocation_id AND
json_extract(record_json, '$.projectId') = project_id AND
json_extract(record_json, '$.reservationDigest') =
reservation_digest AND
json_extract(record_json, '$.completionDigest') = completion_digest AND
json_extract(record_json, '$.effectiveTokens') = effective_tokens AND
((effective_cost_micros IS NULL AND
json_type(record_json, '$.effectiveCostMicros') = 'null') OR
json_extract(record_json, '$.effectiveCostMicros') =
effective_cost_micros) AND
json_extract(record_json, '$.retainedTokenReservation') =
retained_token_reservation AND
json_extract(record_json, '$.retainedCostReservation') =
retained_cost_reservation AND
json_extract(record_json, '$.settledAtMs') = settled_at_ms AND
json_extract(record_json, '$.settlementDigest') = settlement_digest
)
)`;
const localQuotaMigration = defineSqlMigration<LocalMigrationContext>(
LOCAL_MODEL_INVOCATION_QUOTA_MIGRATION_ID,
[
LOCAL_QUOTA_RESERVATION_TABLE_SQL,
`CREATE INDEX ql3_model_invocation_quota_reservation_window_idx
ON "ModelInvocationQuotaReservations"
(project_id, window_start_ms, invocation_id)`,
`CREATE UNIQUE INDEX ql3_model_invocation_quota_reservation_digest_uidx
ON "ModelInvocationQuotaReservations" (reservation_digest)`,
LOCAL_QUOTA_SETTLEMENT_TABLE_SQL,
`CREATE UNIQUE INDEX ql3_model_invocation_quota_settlement_digest_uidx
ON "ModelInvocationQuotaSettlements" (settlement_digest)`,
],
(context, statement) => context.client.exec(statement),
);
const LOCAL_PRICE_QUOTE_TABLE_SQL = `
CREATE TABLE "ModelInvocationPriceQuotes" (
invocation_id TEXT PRIMARY KEY NOT NULL,
project_id TEXT NOT NULL,
model_policy_revision TEXT NOT NULL,
provider TEXT NOT NULL,
model TEXT NOT NULL,
price_revision TEXT NOT NULL,
currency TEXT NOT NULL,
input_micros_per_million_tokens INTEGER NOT NULL,
output_micros_per_million_tokens INTEGER NOT NULL,
max_total_tokens INTEGER NOT NULL,
max_output_tokens INTEGER NOT NULL,
reserved_cost_micros INTEGER NOT NULL,
catalog_digest TEXT NOT NULL,
quote_digest TEXT NOT NULL,
record_json TEXT NOT NULL,
FOREIGN KEY (invocation_id)
REFERENCES "ModelInvocationStarts" (invocation_id) ON DELETE RESTRICT,
CONSTRAINT ql3_model_invocation_price_quote_identity_check CHECK (
length(project_id) BETWEEN 1 AND 128 AND
length(model_policy_revision) BETWEEN 1 AND 128 AND
length(provider) BETWEEN 1 AND 128 AND
length(model) BETWEEN 1 AND 128 AND
length(price_revision) BETWEEN 1 AND 128
),
CONSTRAINT ql3_model_invocation_price_quote_value_check CHECK (
currency = 'USD' AND
input_micros_per_million_tokens BETWEEN 0 AND 1000000000000 AND
output_micros_per_million_tokens BETWEEN 0 AND 1000000000000 AND
max_total_tokens BETWEEN 1 AND 1000000000000 AND
max_output_tokens BETWEEN 1 AND max_total_tokens AND
reserved_cost_micros BETWEEN 0 AND 1000000000000000 AND
length(catalog_digest) = 64 AND
catalog_digest NOT GLOB '*[^0-9a-f]*' AND
length(quote_digest) = 64 AND
quote_digest NOT GLOB '*[^0-9a-f]*'
),
CONSTRAINT ql3_model_invocation_price_quote_json_check CHECK (
length(CAST(record_json AS BLOB)) BETWEEN 2 AND 24576 AND
json_valid(record_json) AND json_type(record_json) = 'object' AND
json_extract(record_json, '$.schema') =
'qinglong/model-invocation-price-quote@v1' AND
json_extract(record_json, '$.invocationId') = invocation_id AND
json_extract(record_json, '$.projectId') = project_id AND
json_extract(record_json, '$.modelPolicyRevision') =
model_policy_revision AND
json_extract(record_json, '$.provider') = provider AND
json_extract(record_json, '$.model') = model AND
json_extract(record_json, '$.priceRevision') = price_revision AND
json_extract(record_json, '$.currency') = currency AND
json_extract(record_json, '$.inputMicrosPerMillionTokens') =
input_micros_per_million_tokens AND
json_extract(record_json, '$.outputMicrosPerMillionTokens') =
output_micros_per_million_tokens AND
json_extract(record_json, '$.maxTotalTokens') = max_total_tokens AND
json_extract(record_json, '$.maxOutputTokens') = max_output_tokens AND
json_extract(record_json, '$.reservedCostMicros') =
reserved_cost_micros AND
json_extract(record_json, '$.catalogDigest') = catalog_digest AND
json_extract(record_json, '$.quoteDigest') = quote_digest
)
)`;
const LOCAL_PRICE_SETTLEMENT_TABLE_SQL = `
CREATE TABLE "ModelInvocationPriceSettlements" (
invocation_id TEXT PRIMARY KEY NOT NULL,
project_id TEXT NOT NULL,
quote_digest TEXT NOT NULL,
completion_digest TEXT NOT NULL,
currency TEXT NOT NULL,
input_tokens INTEGER NOT NULL,
output_tokens INTEGER NOT NULL,
cost_micros INTEGER NOT NULL,
settled_at_ms INTEGER NOT NULL,
settlement_digest TEXT NOT NULL,
record_json TEXT NOT NULL,
FOREIGN KEY (invocation_id, quote_digest)
REFERENCES "ModelInvocationPriceQuotes"
(invocation_id, quote_digest) ON DELETE RESTRICT,
FOREIGN KEY (invocation_id, completion_digest)
REFERENCES "ModelInvocationCompletions"
(invocation_id, completion_digest) ON DELETE RESTRICT,
CONSTRAINT ql3_model_invocation_price_settlement_value_check CHECK (
currency = 'USD' AND
input_tokens >= 0 AND output_tokens >= 0 AND
cost_micros BETWEEN 0 AND 1000000000000000 AND
settled_at_ms >= 0 AND
length(quote_digest) = 64 AND
quote_digest NOT GLOB '*[^0-9a-f]*' AND
length(completion_digest) = 64 AND
completion_digest NOT GLOB '*[^0-9a-f]*' AND
length(settlement_digest) = 64 AND
settlement_digest NOT GLOB '*[^0-9a-f]*'
),
CONSTRAINT ql3_model_invocation_price_settlement_json_check CHECK (
length(CAST(record_json AS BLOB)) BETWEEN 2 AND 24576 AND
json_valid(record_json) AND json_type(record_json) = 'object' AND
json_extract(record_json, '$.schema') =
'qinglong/model-invocation-price-settlement@v1' AND
json_extract(record_json, '$.invocationId') = invocation_id AND
json_extract(record_json, '$.projectId') = project_id AND
json_extract(record_json, '$.quoteDigest') = quote_digest AND
json_extract(record_json, '$.completionDigest') =
completion_digest AND
json_extract(record_json, '$.currency') = currency AND
json_extract(record_json, '$.inputTokens') = input_tokens AND
json_extract(record_json, '$.outputTokens') = output_tokens AND
json_extract(record_json, '$.costMicros') = cost_micros AND
json_extract(record_json, '$.settledAtMs') = settled_at_ms AND
json_extract(record_json, '$.settlementDigest') = settlement_digest
)
)`;
const localPricingMigration = defineSqlMigration<LocalMigrationContext>(
LOCAL_MODEL_INVOCATION_PRICING_MIGRATION_ID,
[
LOCAL_PRICE_QUOTE_TABLE_SQL,
`CREATE UNIQUE INDEX ql3_model_invocation_price_quote_identity_digest_uidx
ON "ModelInvocationPriceQuotes" (invocation_id, quote_digest)`,
`CREATE UNIQUE INDEX ql3_model_invocation_price_quote_digest_uidx
ON "ModelInvocationPriceQuotes" (quote_digest)`,
LOCAL_PRICE_SETTLEMENT_TABLE_SQL,
`CREATE UNIQUE INDEX ql3_model_invocation_price_settlement_digest_uidx
ON "ModelInvocationPriceSettlements" (settlement_digest)`,
],
(context, statement) => context.client.exec(statement),
);
export const sqliteUsagePricingMigrations = Object.freeze([
localUsageMigration,
localQuotaMigration,
localPricingMigration,
]);
@@ -0,0 +1,3 @@
export * from './model-invocation/identities';
export * from './model-invocation/postgres';
export * from './model-invocation/sqlite';