mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-21 01:00:24 +08:00
224 lines
6.8 KiB
JavaScript
224 lines
6.8 KiB
JavaScript
const assert = require('node:assert/strict');
|
|
const test = require('node:test');
|
|
|
|
const {
|
|
pluginPackageAutomationPublicationDigest,
|
|
} = require('@qinglong/runtime-core/plugin-package-automation-publication');
|
|
const {
|
|
preparePluginPackagePromptExecution,
|
|
} = require('../dist/prompt/pluginPackagePromptExecution.js');
|
|
const {
|
|
createPluginPackagePromptOutputArtifact,
|
|
} = require('../dist/prompt-output/pluginPackagePromptOutputArtifact.js');
|
|
const {
|
|
PostgresPluginPackagePromptOutputArtifactRepository,
|
|
} = require('../dist/prompt-output/storage/postgresPluginPackagePromptOutputArtifactRepository.js');
|
|
|
|
function publication() {
|
|
const unsigned = {
|
|
schema: 'qinglong/plugin-package-automation-publication@v1',
|
|
target: {
|
|
projectId: 'project-a',
|
|
packageName: 'package-a',
|
|
installationId: 'installation-a',
|
|
lockDigest: '1'.repeat(64),
|
|
generation: 1,
|
|
generationDigest: '2'.repeat(64),
|
|
materializedRevisionDigest: '3'.repeat(64),
|
|
},
|
|
state: 'active',
|
|
version: 1,
|
|
previousPublicationDigest: null,
|
|
lifecycleEventDigest: null,
|
|
definitions: {
|
|
workflows: [],
|
|
prompts: [
|
|
{
|
|
schema: 'qinglong/plugin-package-prompt-resource@v1',
|
|
id: 'summary',
|
|
name: 'Summary',
|
|
template: 'Summarize {{subject}}.',
|
|
parameters: [{ name: 'subject', required: true }],
|
|
},
|
|
],
|
|
},
|
|
publishedAtMs: 1_000,
|
|
};
|
|
return {
|
|
...unsigned,
|
|
publicationDigest: pluginPackageAutomationPublicationDigest(unsigned),
|
|
};
|
|
}
|
|
|
|
function prepared(output) {
|
|
const active = publication();
|
|
return preparePluginPackagePromptExecution({
|
|
publication: active,
|
|
expectedPublicationDigest: active.publicationDigest,
|
|
promptId: 'summary',
|
|
requestId: 'prompt-request-a',
|
|
traceId: 'trace-a',
|
|
requestedBySubject: { type: 'user', id: 'user-a' },
|
|
policyFence: { projectVersion: 1, bindingVersion: 1 },
|
|
parameters: { subject: 'private input' },
|
|
provider: 'openai-compatible',
|
|
model: 'model-a',
|
|
maxOutputTokens: 64,
|
|
plannedAtMs: 2_000,
|
|
deadlineAtMs: 62_000,
|
|
...(output === undefined ? {} : { output }),
|
|
});
|
|
}
|
|
|
|
function row(artifact) {
|
|
return {
|
|
artifactId: artifact.artifactId,
|
|
projectId: artifact.projectId,
|
|
runId: artifact.runId,
|
|
stepRunId: artifact.stepRunId,
|
|
invocationId: artifact.invocationId,
|
|
requestedByType: artifact.requestedBy.type,
|
|
requestedById: artifact.requestedBy.id,
|
|
provider: artifact.provider,
|
|
model: artifact.model,
|
|
contentDigest: artifact.contentDigest,
|
|
outputBytes: artifact.outputBytes,
|
|
retentionPolicyRevision: artifact.retentionPolicy.revision,
|
|
retentionMs: String(artifact.retentionPolicy.retentionMs),
|
|
retentionPolicyDigest: artifact.retentionPolicyDigest,
|
|
retentionEligibleAtMs: String(artifact.retentionEligibleAtMs),
|
|
keyId: artifact.keyId,
|
|
algorithm: artifact.algorithm,
|
|
plaintextBytes: artifact.plaintextBytes,
|
|
sealedAtMs: String(artifact.sealedAtMs),
|
|
artifactDigest: artifact.artifactDigest,
|
|
artifactJson: artifact,
|
|
};
|
|
}
|
|
|
|
function fakePool(plan) {
|
|
const queries = [];
|
|
let stored = null;
|
|
const client = {
|
|
async query(sql, parameters = []) {
|
|
queries.push(sql);
|
|
if (sql.includes('model_invocation_prompt_admissions')) {
|
|
return { rows: [{ planJson: plan }] };
|
|
}
|
|
if (
|
|
sql.startsWith('SELECT artifact_id') &&
|
|
sql.includes('model_invocation_prompt_output_artifacts')
|
|
) {
|
|
return {
|
|
rows: stored && stored.artifactId === parameters[0] ? [stored] : [],
|
|
};
|
|
}
|
|
if (
|
|
sql.includes('INSERT INTO') &&
|
|
sql.includes('prompt_output_artifacts')
|
|
) {
|
|
const artifact = JSON.parse(parameters[20]);
|
|
stored = row(artifact);
|
|
return { rows: [] };
|
|
}
|
|
return { rows: [] };
|
|
},
|
|
release() {},
|
|
};
|
|
return {
|
|
queries,
|
|
async connect() {
|
|
return client;
|
|
},
|
|
async query(sql, parameters) {
|
|
return client.query(sql, parameters);
|
|
},
|
|
};
|
|
}
|
|
|
|
test('PostgreSQL Prompt output Artifact is immutable and plan-bound', async () => {
|
|
const durable = prepared({
|
|
mode: 'durable_artifact',
|
|
retentionPolicy: { revision: 'cluster-v1', retentionMs: 86_400_000 },
|
|
});
|
|
const artifact = createPluginPackagePromptOutputArtifact(
|
|
{
|
|
projectId: durable.plan.target.projectId,
|
|
runId: durable.plan.runId,
|
|
stepRunId: durable.plan.stepRunId,
|
|
invocationId: durable.plan.invocationId,
|
|
requestedBy: durable.plan.requestedBySubject,
|
|
result: {
|
|
provider: durable.plan.provider,
|
|
model: durable.plan.model,
|
|
text: 'private PostgreSQL Artifact output',
|
|
finishReason: 'stop',
|
|
usage: { inputTokens: 2, outputTokens: 3, totalTokens: 5 },
|
|
},
|
|
retentionPolicy: durable.plan.output.retentionPolicy,
|
|
keyId: 'cluster-key-1',
|
|
key: Buffer.alloc(32, 7),
|
|
sealedAtMs: 3_000,
|
|
},
|
|
() => Buffer.alloc(12, 9),
|
|
);
|
|
const pool = fakePool(durable.plan);
|
|
const repository = new PostgresPluginPackagePromptOutputArtifactRepository(
|
|
pool,
|
|
);
|
|
|
|
assert.deepEqual(await repository.put(artifact), { status: 'inserted' });
|
|
assert.deepEqual(await repository.put(artifact), { status: 'existing' });
|
|
assert.deepEqual(await repository.find(artifact.artifactId), artifact);
|
|
assert.equal(pool.queries.includes('BEGIN'), true);
|
|
assert.equal(
|
|
pool.queries.includes('SET TRANSACTION ISOLATION LEVEL SERIALIZABLE'),
|
|
true,
|
|
);
|
|
assert.equal(
|
|
pool.queries.some((sql) => /FOR (?:UPDATE|SHARE)/.test(sql)),
|
|
false,
|
|
);
|
|
assert.equal(
|
|
JSON.stringify(row(artifact)).includes(artifact.ciphertext),
|
|
true,
|
|
);
|
|
assert.equal(
|
|
JSON.stringify(row(artifact)).includes('private PostgreSQL'),
|
|
false,
|
|
);
|
|
|
|
const live = prepared();
|
|
const livePool = fakePool(live.plan);
|
|
const liveRepository =
|
|
new PostgresPluginPackagePromptOutputArtifactRepository(livePool);
|
|
const liveArtifact = createPluginPackagePromptOutputArtifact(
|
|
{
|
|
projectId: live.plan.target.projectId,
|
|
runId: live.plan.runId,
|
|
stepRunId: live.plan.stepRunId,
|
|
invocationId: live.plan.invocationId,
|
|
requestedBy: live.plan.requestedBySubject,
|
|
result: {
|
|
provider: live.plan.provider,
|
|
model: live.plan.model,
|
|
text: 'must remain live only',
|
|
finishReason: 'stop',
|
|
usage: { inputTokens: 1, outputTokens: 1, totalTokens: 2 },
|
|
},
|
|
retentionPolicy: { revision: 'cluster-v1', retentionMs: 86_400_000 },
|
|
keyId: 'cluster-key-1',
|
|
key: Buffer.alloc(32, 7),
|
|
sealedAtMs: 3_000,
|
|
},
|
|
() => Buffer.alloc(12, 9),
|
|
);
|
|
await assert.rejects(liveRepository.put(liveArtifact), {
|
|
code: 'PLUGIN_PACKAGE_PROMPT_OUTPUT_ARTIFACT_CONFLICT',
|
|
});
|
|
assert.equal(
|
|
livePool.queries.some((sql) => sql.includes('INSERT INTO')),
|
|
false,
|
|
);
|
|
});
|