substrai-promptops 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.d.ts +32 -0
- package/dist/client.js +102 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +25 -0
- package/dist/prompt.d.ts +56 -0
- package/dist/prompt.js +74 -0
- package/dist/schema.d.ts +28 -0
- package/dist/schema.js +100 -0
- package/dist/testing.d.ts +42 -0
- package/dist/testing.js +73 -0
- package/dist/version.d.ts +27 -0
- package/dist/version.js +88 -0
- package/package.json +42 -0
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PromptOps client for runtime prompt invocation.
|
|
3
|
+
*/
|
|
4
|
+
import { PromptDefinition, Prompt } from "./prompt";
|
|
5
|
+
export interface InvocationResult {
|
|
6
|
+
output: any;
|
|
7
|
+
promptName: string;
|
|
8
|
+
version: string;
|
|
9
|
+
model: string;
|
|
10
|
+
latencyMs: number;
|
|
11
|
+
inputTokens: number;
|
|
12
|
+
outputTokens: number;
|
|
13
|
+
totalTokens: number;
|
|
14
|
+
cost: number;
|
|
15
|
+
environment: string;
|
|
16
|
+
success: boolean;
|
|
17
|
+
errors: string[];
|
|
18
|
+
}
|
|
19
|
+
export declare class PromptClient {
|
|
20
|
+
private env;
|
|
21
|
+
private registry;
|
|
22
|
+
constructor(options?: {
|
|
23
|
+
env?: string;
|
|
24
|
+
});
|
|
25
|
+
register(definition: PromptDefinition): void;
|
|
26
|
+
invoke(promptName: string, inputs: Record<string, any>, options?: {
|
|
27
|
+
version?: string;
|
|
28
|
+
model?: string;
|
|
29
|
+
}): InvocationResult;
|
|
30
|
+
get(promptName: string, version?: string): Prompt;
|
|
31
|
+
listPrompts(): string[];
|
|
32
|
+
}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* PromptOps client for runtime prompt invocation.
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.PromptClient = void 0;
|
|
7
|
+
const prompt_1 = require("./prompt");
|
|
8
|
+
const version_1 = require("./version");
|
|
9
|
+
class PromptClient {
|
|
10
|
+
constructor(options = {}) {
|
|
11
|
+
this.env = options.env || "dev";
|
|
12
|
+
this.registry = new Map();
|
|
13
|
+
}
|
|
14
|
+
register(definition) {
|
|
15
|
+
if (!this.registry.has(definition.name)) {
|
|
16
|
+
this.registry.set(definition.name, new Map());
|
|
17
|
+
}
|
|
18
|
+
this.registry.get(definition.name).set(definition.version.toString(), definition);
|
|
19
|
+
}
|
|
20
|
+
invoke(promptName, inputs, options = {}) {
|
|
21
|
+
const start = Date.now();
|
|
22
|
+
const version = options.version || "latest";
|
|
23
|
+
try {
|
|
24
|
+
const definitions = this.registry.get(promptName);
|
|
25
|
+
if (!definitions) {
|
|
26
|
+
throw new Error(`Prompt '${promptName}' not found`);
|
|
27
|
+
}
|
|
28
|
+
const range = new version_1.VersionRange(version);
|
|
29
|
+
let matched;
|
|
30
|
+
let matchedVersion;
|
|
31
|
+
for (const [verStr, def] of definitions) {
|
|
32
|
+
const ver = version_1.PromptVersion.parse(verStr);
|
|
33
|
+
if (range.matches(ver)) {
|
|
34
|
+
if (!matchedVersion || ver.compareTo(matchedVersion) > 0) {
|
|
35
|
+
matched = def;
|
|
36
|
+
matchedVersion = ver;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (!matched || !matchedVersion) {
|
|
41
|
+
throw new Error(`No version matching '${version}' for prompt '${promptName}'`);
|
|
42
|
+
}
|
|
43
|
+
const rendered = matched.render(inputs);
|
|
44
|
+
const inputTokens = Math.ceil(rendered.length / 4);
|
|
45
|
+
const outputTokens = Math.floor(matched.defaultModel.maxTokens / 2);
|
|
46
|
+
const cost = matched.estimateCost(inputs);
|
|
47
|
+
const latencyMs = Date.now() - start;
|
|
48
|
+
return {
|
|
49
|
+
output: rendered,
|
|
50
|
+
promptName,
|
|
51
|
+
version: matchedVersion.toString(),
|
|
52
|
+
model: matched.defaultModel.provider,
|
|
53
|
+
latencyMs,
|
|
54
|
+
inputTokens,
|
|
55
|
+
outputTokens,
|
|
56
|
+
totalTokens: inputTokens + outputTokens,
|
|
57
|
+
cost,
|
|
58
|
+
environment: this.env,
|
|
59
|
+
success: true,
|
|
60
|
+
errors: [],
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
catch (e) {
|
|
64
|
+
return {
|
|
65
|
+
output: null,
|
|
66
|
+
promptName,
|
|
67
|
+
version: "0.0.0",
|
|
68
|
+
model: "unknown",
|
|
69
|
+
latencyMs: Date.now() - start,
|
|
70
|
+
inputTokens: 0,
|
|
71
|
+
outputTokens: 0,
|
|
72
|
+
totalTokens: 0,
|
|
73
|
+
cost: 0,
|
|
74
|
+
environment: this.env,
|
|
75
|
+
success: false,
|
|
76
|
+
errors: [e.message],
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
get(promptName, version = "latest") {
|
|
81
|
+
const definitions = this.registry.get(promptName);
|
|
82
|
+
if (!definitions) {
|
|
83
|
+
throw new Error(`Prompt '${promptName}' not found`);
|
|
84
|
+
}
|
|
85
|
+
const range = new version_1.VersionRange(version);
|
|
86
|
+
let matched;
|
|
87
|
+
for (const [verStr, def] of definitions) {
|
|
88
|
+
const ver = version_1.PromptVersion.parse(verStr);
|
|
89
|
+
if (range.matches(ver)) {
|
|
90
|
+
matched = def;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (!matched) {
|
|
94
|
+
throw new Error(`No version matching '${version}' for prompt '${promptName}'`);
|
|
95
|
+
}
|
|
96
|
+
return new prompt_1.Prompt(matched);
|
|
97
|
+
}
|
|
98
|
+
listPrompts() {
|
|
99
|
+
return Array.from(this.registry.keys());
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
exports.PromptClient = PromptClient;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PromptOps - Infrastructure-as-Code for Prompt Engineering
|
|
3
|
+
*
|
|
4
|
+
* The first framework for managing prompts as versioned, tested,
|
|
5
|
+
* deployed infrastructure with semantic versioning, environment
|
|
6
|
+
* promotion, regression testing, and multi-model targeting.
|
|
7
|
+
*
|
|
8
|
+
* @packageDocumentation
|
|
9
|
+
*/
|
|
10
|
+
export { PromptVersion, VersionRange } from "./version";
|
|
11
|
+
export { PromptDefinition, ModelConfig, Prompt } from "./prompt";
|
|
12
|
+
export { InputSchema, OutputSchema, FieldSchema } from "./schema";
|
|
13
|
+
export { PromptClient, InvocationResult } from "./client";
|
|
14
|
+
export { TestRunner, TestResult, TestSuite } from "./testing";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* PromptOps - Infrastructure-as-Code for Prompt Engineering
|
|
4
|
+
*
|
|
5
|
+
* The first framework for managing prompts as versioned, tested,
|
|
6
|
+
* deployed infrastructure with semantic versioning, environment
|
|
7
|
+
* promotion, regression testing, and multi-model targeting.
|
|
8
|
+
*
|
|
9
|
+
* @packageDocumentation
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.TestRunner = exports.PromptClient = exports.OutputSchema = exports.InputSchema = exports.Prompt = exports.PromptDefinition = exports.VersionRange = exports.PromptVersion = void 0;
|
|
13
|
+
var version_1 = require("./version");
|
|
14
|
+
Object.defineProperty(exports, "PromptVersion", { enumerable: true, get: function () { return version_1.PromptVersion; } });
|
|
15
|
+
Object.defineProperty(exports, "VersionRange", { enumerable: true, get: function () { return version_1.VersionRange; } });
|
|
16
|
+
var prompt_1 = require("./prompt");
|
|
17
|
+
Object.defineProperty(exports, "PromptDefinition", { enumerable: true, get: function () { return prompt_1.PromptDefinition; } });
|
|
18
|
+
Object.defineProperty(exports, "Prompt", { enumerable: true, get: function () { return prompt_1.Prompt; } });
|
|
19
|
+
var schema_1 = require("./schema");
|
|
20
|
+
Object.defineProperty(exports, "InputSchema", { enumerable: true, get: function () { return schema_1.InputSchema; } });
|
|
21
|
+
Object.defineProperty(exports, "OutputSchema", { enumerable: true, get: function () { return schema_1.OutputSchema; } });
|
|
22
|
+
var client_1 = require("./client");
|
|
23
|
+
Object.defineProperty(exports, "PromptClient", { enumerable: true, get: function () { return client_1.PromptClient; } });
|
|
24
|
+
var testing_1 = require("./testing");
|
|
25
|
+
Object.defineProperty(exports, "TestRunner", { enumerable: true, get: function () { return testing_1.TestRunner; } });
|
package/dist/prompt.d.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prompt definition parser and model.
|
|
3
|
+
*/
|
|
4
|
+
import { PromptVersion } from "./version";
|
|
5
|
+
import { InputSchema, OutputSchema } from "./schema";
|
|
6
|
+
export interface ModelConfig {
|
|
7
|
+
provider: string;
|
|
8
|
+
template?: string;
|
|
9
|
+
temperature: number;
|
|
10
|
+
maxTokens: number;
|
|
11
|
+
topP: number;
|
|
12
|
+
stopSequences: string[];
|
|
13
|
+
}
|
|
14
|
+
export interface PromptDefinitionData {
|
|
15
|
+
name: string;
|
|
16
|
+
version: string;
|
|
17
|
+
description?: string;
|
|
18
|
+
template: string;
|
|
19
|
+
model?: {
|
|
20
|
+
default?: string;
|
|
21
|
+
fallback?: string;
|
|
22
|
+
variants?: Record<string, any>;
|
|
23
|
+
};
|
|
24
|
+
input?: {
|
|
25
|
+
schema?: Record<string, any>;
|
|
26
|
+
};
|
|
27
|
+
output?: {
|
|
28
|
+
schema?: Record<string, any>;
|
|
29
|
+
};
|
|
30
|
+
settings?: Record<string, any>;
|
|
31
|
+
metadata?: Record<string, any>;
|
|
32
|
+
}
|
|
33
|
+
export declare class PromptDefinition {
|
|
34
|
+
readonly name: string;
|
|
35
|
+
readonly version: PromptVersion;
|
|
36
|
+
readonly description: string;
|
|
37
|
+
readonly template: string;
|
|
38
|
+
readonly inputSchema: InputSchema;
|
|
39
|
+
readonly outputSchema: OutputSchema;
|
|
40
|
+
readonly defaultModel: ModelConfig;
|
|
41
|
+
readonly metadata: Record<string, any>;
|
|
42
|
+
constructor(data: PromptDefinitionData);
|
|
43
|
+
render(inputs: Record<string, any>): string;
|
|
44
|
+
estimateTokens(inputs: Record<string, any>): number;
|
|
45
|
+
estimateCost(inputs: Record<string, any>): number;
|
|
46
|
+
}
|
|
47
|
+
export declare class Prompt {
|
|
48
|
+
readonly definition: PromptDefinition;
|
|
49
|
+
readonly name: string;
|
|
50
|
+
readonly version: PromptVersion;
|
|
51
|
+
readonly template: string;
|
|
52
|
+
constructor(definition: PromptDefinition);
|
|
53
|
+
render(inputs: Record<string, any>): string;
|
|
54
|
+
validateInputs(inputs: Record<string, any>): string[];
|
|
55
|
+
estimateCost(inputs: Record<string, any>): number;
|
|
56
|
+
}
|
package/dist/prompt.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Prompt definition parser and model.
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.Prompt = exports.PromptDefinition = void 0;
|
|
7
|
+
const version_1 = require("./version");
|
|
8
|
+
const schema_1 = require("./schema");
|
|
9
|
+
class PromptDefinition {
|
|
10
|
+
constructor(data) {
|
|
11
|
+
this.name = data.name;
|
|
12
|
+
this.version = version_1.PromptVersion.parse(data.version || "0.1.0");
|
|
13
|
+
this.description = data.description || "";
|
|
14
|
+
this.template = data.template || "";
|
|
15
|
+
this.inputSchema = schema_1.InputSchema.fromDict(data.input?.schema || {});
|
|
16
|
+
this.outputSchema = schema_1.OutputSchema.fromDict(data.output?.schema || {});
|
|
17
|
+
this.defaultModel = {
|
|
18
|
+
provider: data.model?.default || "bedrock/claude-3-haiku",
|
|
19
|
+
temperature: data.settings?.temperature || 0.7,
|
|
20
|
+
maxTokens: data.settings?.max_tokens || 2000,
|
|
21
|
+
topP: data.settings?.top_p || 1.0,
|
|
22
|
+
stopSequences: data.settings?.stop_sequences || [],
|
|
23
|
+
};
|
|
24
|
+
this.metadata = data.metadata || {};
|
|
25
|
+
}
|
|
26
|
+
render(inputs) {
|
|
27
|
+
const withDefaults = this.inputSchema.applyDefaults(inputs);
|
|
28
|
+
const errors = this.inputSchema.validate(withDefaults);
|
|
29
|
+
if (errors.length > 0) {
|
|
30
|
+
throw new Error(`Input validation failed: ${errors.join("; ")}`);
|
|
31
|
+
}
|
|
32
|
+
let rendered = this.template;
|
|
33
|
+
for (const [key, value] of Object.entries(withDefaults)) {
|
|
34
|
+
rendered = rendered.replace(new RegExp(`\\{${key}\\}`, "g"), String(value));
|
|
35
|
+
}
|
|
36
|
+
return rendered;
|
|
37
|
+
}
|
|
38
|
+
estimateTokens(inputs) {
|
|
39
|
+
const rendered = this.render(inputs);
|
|
40
|
+
return Math.ceil(rendered.length / 4);
|
|
41
|
+
}
|
|
42
|
+
estimateCost(inputs) {
|
|
43
|
+
const pricing = {
|
|
44
|
+
"bedrock/claude-3-haiku": { input: 0.00025, output: 0.00125 },
|
|
45
|
+
"bedrock/claude-3-sonnet": { input: 0.003, output: 0.015 },
|
|
46
|
+
"bedrock/amazon-titan-text-lite": { input: 0.00015, output: 0.00015 },
|
|
47
|
+
"openai/gpt-4o-mini": { input: 0.00015, output: 0.0006 },
|
|
48
|
+
};
|
|
49
|
+
const modelPricing = pricing[this.defaultModel.provider] || { input: 0.001, output: 0.002 };
|
|
50
|
+
const inputTokens = this.estimateTokens(inputs);
|
|
51
|
+
const outputTokens = Math.floor(this.defaultModel.maxTokens / 2);
|
|
52
|
+
return (inputTokens / 1000) * modelPricing.input + (outputTokens / 1000) * modelPricing.output;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
exports.PromptDefinition = PromptDefinition;
|
|
56
|
+
class Prompt {
|
|
57
|
+
constructor(definition) {
|
|
58
|
+
this.definition = definition;
|
|
59
|
+
this.name = definition.name;
|
|
60
|
+
this.version = definition.version;
|
|
61
|
+
this.template = definition.template;
|
|
62
|
+
}
|
|
63
|
+
render(inputs) {
|
|
64
|
+
return this.definition.render(inputs);
|
|
65
|
+
}
|
|
66
|
+
validateInputs(inputs) {
|
|
67
|
+
const withDefaults = this.definition.inputSchema.applyDefaults(inputs);
|
|
68
|
+
return this.definition.inputSchema.validate(withDefaults);
|
|
69
|
+
}
|
|
70
|
+
estimateCost(inputs) {
|
|
71
|
+
return this.definition.estimateCost(inputs);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
exports.Prompt = Prompt;
|
package/dist/schema.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema validation for prompt inputs and outputs.
|
|
3
|
+
*/
|
|
4
|
+
export interface FieldSchema {
|
|
5
|
+
name: string;
|
|
6
|
+
type: string;
|
|
7
|
+
required?: boolean;
|
|
8
|
+
default?: any;
|
|
9
|
+
minValue?: number;
|
|
10
|
+
maxValue?: number;
|
|
11
|
+
minLength?: number;
|
|
12
|
+
maxLength?: number;
|
|
13
|
+
values?: string[];
|
|
14
|
+
items?: string;
|
|
15
|
+
}
|
|
16
|
+
export declare class InputSchema {
|
|
17
|
+
readonly fields: Map<string, FieldSchema>;
|
|
18
|
+
constructor(fields: Map<string, FieldSchema>);
|
|
19
|
+
static fromDict(schemaDict: Record<string, any>): InputSchema;
|
|
20
|
+
validate(inputs: Record<string, any>): string[];
|
|
21
|
+
applyDefaults(inputs: Record<string, any>): Record<string, any>;
|
|
22
|
+
}
|
|
23
|
+
export declare class OutputSchema {
|
|
24
|
+
readonly fields: Map<string, FieldSchema>;
|
|
25
|
+
constructor(fields: Map<string, FieldSchema>);
|
|
26
|
+
static fromDict(schemaDict: Record<string, any>): OutputSchema;
|
|
27
|
+
validate(output: Record<string, any>): string[];
|
|
28
|
+
}
|
package/dist/schema.js
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Schema validation for prompt inputs and outputs.
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.OutputSchema = exports.InputSchema = void 0;
|
|
7
|
+
class InputSchema {
|
|
8
|
+
constructor(fields) {
|
|
9
|
+
this.fields = fields;
|
|
10
|
+
}
|
|
11
|
+
static fromDict(schemaDict) {
|
|
12
|
+
const fields = new Map();
|
|
13
|
+
for (const [name, config] of Object.entries(schemaDict)) {
|
|
14
|
+
if (typeof config === "object" && config !== null) {
|
|
15
|
+
fields.set(name, {
|
|
16
|
+
name,
|
|
17
|
+
type: config.type || "string",
|
|
18
|
+
required: config.required !== false,
|
|
19
|
+
default: config.default,
|
|
20
|
+
minValue: config.min,
|
|
21
|
+
maxValue: config.max,
|
|
22
|
+
minLength: config.min_length,
|
|
23
|
+
maxLength: config.max_length,
|
|
24
|
+
values: config.values,
|
|
25
|
+
items: config.items,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
fields.set(name, { name, type: String(config), required: true });
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return new InputSchema(fields);
|
|
33
|
+
}
|
|
34
|
+
validate(inputs) {
|
|
35
|
+
const errors = [];
|
|
36
|
+
for (const [name, field] of this.fields) {
|
|
37
|
+
const value = inputs[name] ?? field.default;
|
|
38
|
+
if (value === undefined || value === null) {
|
|
39
|
+
if (field.required && field.default === undefined) {
|
|
40
|
+
errors.push(`Field '${name}' is required`);
|
|
41
|
+
}
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (field.type === "enum" && field.values && !field.values.includes(value)) {
|
|
45
|
+
errors.push(`Field '${name}' must be one of [${field.values.join(", ")}]`);
|
|
46
|
+
}
|
|
47
|
+
if (typeof value === "number") {
|
|
48
|
+
if (field.minValue !== undefined && value < field.minValue) {
|
|
49
|
+
errors.push(`Field '${name}' must be >= ${field.minValue}`);
|
|
50
|
+
}
|
|
51
|
+
if (field.maxValue !== undefined && value > field.maxValue) {
|
|
52
|
+
errors.push(`Field '${name}' must be <= ${field.maxValue}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (typeof value === "string") {
|
|
56
|
+
if (field.maxLength !== undefined && value.length > field.maxLength) {
|
|
57
|
+
errors.push(`Field '${name}' must be at most ${field.maxLength} chars`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return errors;
|
|
62
|
+
}
|
|
63
|
+
applyDefaults(inputs) {
|
|
64
|
+
const result = { ...inputs };
|
|
65
|
+
for (const [name, field] of this.fields) {
|
|
66
|
+
if (!(name in result) && field.default !== undefined) {
|
|
67
|
+
result[name] = field.default;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return result;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
exports.InputSchema = InputSchema;
|
|
74
|
+
class OutputSchema {
|
|
75
|
+
constructor(fields) {
|
|
76
|
+
this.fields = fields;
|
|
77
|
+
}
|
|
78
|
+
static fromDict(schemaDict) {
|
|
79
|
+
const fields = new Map();
|
|
80
|
+
for (const [name, config] of Object.entries(schemaDict)) {
|
|
81
|
+
if (typeof config === "object" && config !== null) {
|
|
82
|
+
fields.set(name, { name, type: config.type || "string", required: false });
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
fields.set(name, { name, type: String(config), required: false });
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return new OutputSchema(fields);
|
|
89
|
+
}
|
|
90
|
+
validate(output) {
|
|
91
|
+
const errors = [];
|
|
92
|
+
for (const [name, field] of this.fields) {
|
|
93
|
+
if (field.required && !(name in output)) {
|
|
94
|
+
errors.push(`Missing required output field: '${name}'`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return errors;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
exports.OutputSchema = OutputSchema;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Testing framework for PromptOps prompts.
|
|
3
|
+
*/
|
|
4
|
+
export interface TestCase {
|
|
5
|
+
name: string;
|
|
6
|
+
inputs: Record<string, any>;
|
|
7
|
+
assertions: AssertionConfig[];
|
|
8
|
+
}
|
|
9
|
+
export interface AssertionConfig {
|
|
10
|
+
type: string;
|
|
11
|
+
field?: string;
|
|
12
|
+
value?: any;
|
|
13
|
+
values?: string[];
|
|
14
|
+
keywords?: string[];
|
|
15
|
+
min?: number;
|
|
16
|
+
max?: number;
|
|
17
|
+
threshold?: number;
|
|
18
|
+
}
|
|
19
|
+
export interface AssertionResult {
|
|
20
|
+
passed: boolean;
|
|
21
|
+
assertionType: string;
|
|
22
|
+
message: string;
|
|
23
|
+
}
|
|
24
|
+
export interface TestResult {
|
|
25
|
+
testName: string;
|
|
26
|
+
promptName: string;
|
|
27
|
+
passed: boolean;
|
|
28
|
+
assertionResults: AssertionResult[];
|
|
29
|
+
error?: string;
|
|
30
|
+
}
|
|
31
|
+
export interface TestSuite {
|
|
32
|
+
promptName: string;
|
|
33
|
+
testCases: TestCase[];
|
|
34
|
+
passThreshold: number;
|
|
35
|
+
}
|
|
36
|
+
export declare class TestRunner {
|
|
37
|
+
runAssertion(config: AssertionConfig, output: any): AssertionResult;
|
|
38
|
+
private checkSchemaValid;
|
|
39
|
+
private checkMaxLength;
|
|
40
|
+
private checkContainsKeywords;
|
|
41
|
+
private checkDoesNotContain;
|
|
42
|
+
}
|
package/dist/testing.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Testing framework for PromptOps prompts.
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.TestRunner = void 0;
|
|
7
|
+
class TestRunner {
|
|
8
|
+
runAssertion(config, output) {
|
|
9
|
+
switch (config.type) {
|
|
10
|
+
case "schema_valid":
|
|
11
|
+
return this.checkSchemaValid(output);
|
|
12
|
+
case "max_length":
|
|
13
|
+
return this.checkMaxLength(config, output);
|
|
14
|
+
case "contains_keywords":
|
|
15
|
+
return this.checkContainsKeywords(config, output);
|
|
16
|
+
case "does_not_contain":
|
|
17
|
+
return this.checkDoesNotContain(config, output);
|
|
18
|
+
case "cost_under":
|
|
19
|
+
return { passed: true, assertionType: "cost_under", message: "Cost check passed" };
|
|
20
|
+
default:
|
|
21
|
+
return { passed: false, assertionType: config.type, message: `Unknown assertion: ${config.type}` };
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
checkSchemaValid(output) {
|
|
25
|
+
if (typeof output === "object" && output !== null) {
|
|
26
|
+
return { passed: true, assertionType: "schema_valid", message: "Valid object" };
|
|
27
|
+
}
|
|
28
|
+
if (typeof output === "string") {
|
|
29
|
+
try {
|
|
30
|
+
JSON.parse(output);
|
|
31
|
+
return { passed: true, assertionType: "schema_valid", message: "Valid JSON" };
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return { passed: false, assertionType: "schema_valid", message: "Not valid JSON" };
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return { passed: false, assertionType: "schema_valid", message: "Invalid output type" };
|
|
38
|
+
}
|
|
39
|
+
checkMaxLength(config, output) {
|
|
40
|
+
const text = config.field && typeof output === "object" ? output[config.field] : output;
|
|
41
|
+
const wordCount = String(text || "").split(/\s+/).length;
|
|
42
|
+
const maxWords = config.value || 100;
|
|
43
|
+
const passed = wordCount <= maxWords;
|
|
44
|
+
return {
|
|
45
|
+
passed,
|
|
46
|
+
assertionType: "max_length",
|
|
47
|
+
message: `Word count ${wordCount} ${passed ? "<=" : ">"} ${maxWords}`,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
checkContainsKeywords(config, output) {
|
|
51
|
+
const text = config.field && typeof output === "object" ? output[config.field] : output;
|
|
52
|
+
const textLower = String(text || "").toLowerCase();
|
|
53
|
+
const keywords = config.keywords || [];
|
|
54
|
+
const missing = keywords.filter((kw) => !textLower.includes(kw.toLowerCase()));
|
|
55
|
+
return {
|
|
56
|
+
passed: missing.length === 0,
|
|
57
|
+
assertionType: "contains_keywords",
|
|
58
|
+
message: missing.length === 0 ? "All keywords found" : `Missing: ${missing.join(", ")}`,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
checkDoesNotContain(config, output) {
|
|
62
|
+
const text = config.field && typeof output === "object" ? output[config.field] : output;
|
|
63
|
+
const textLower = String(text || "").toLowerCase();
|
|
64
|
+
const forbidden = config.values || [];
|
|
65
|
+
const found = forbidden.filter((v) => textLower.includes(v.toLowerCase()));
|
|
66
|
+
return {
|
|
67
|
+
passed: found.length === 0,
|
|
68
|
+
assertionType: "does_not_contain",
|
|
69
|
+
message: found.length === 0 ? "No forbidden content" : `Found: ${found.join(", ")}`,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
exports.TestRunner = TestRunner;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Semantic versioning engine for prompts.
|
|
3
|
+
*/
|
|
4
|
+
export declare class PromptVersion {
|
|
5
|
+
readonly major: number;
|
|
6
|
+
readonly minor: number;
|
|
7
|
+
readonly patch: number;
|
|
8
|
+
readonly prerelease?: string;
|
|
9
|
+
private static PATTERN;
|
|
10
|
+
constructor(major: number, minor: number, patch: number, prerelease?: string);
|
|
11
|
+
static parse(versionStr: string): PromptVersion;
|
|
12
|
+
bumpPatch(): PromptVersion;
|
|
13
|
+
bumpMinor(): PromptVersion;
|
|
14
|
+
bumpMajor(): PromptVersion;
|
|
15
|
+
isPrerelease(): boolean;
|
|
16
|
+
isCompatibleWith(other: PromptVersion): boolean;
|
|
17
|
+
compareTo(other: PromptVersion): number;
|
|
18
|
+
toString(): string;
|
|
19
|
+
}
|
|
20
|
+
export declare class VersionRange {
|
|
21
|
+
readonly spec: string;
|
|
22
|
+
constructor(spec: string);
|
|
23
|
+
static latest(): VersionRange;
|
|
24
|
+
static exact(version: string): VersionRange;
|
|
25
|
+
static compatible(version: string): VersionRange;
|
|
26
|
+
matches(version: PromptVersion): boolean;
|
|
27
|
+
}
|
package/dist/version.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Semantic versioning engine for prompts.
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.VersionRange = exports.PromptVersion = void 0;
|
|
7
|
+
class PromptVersion {
|
|
8
|
+
constructor(major, minor, patch, prerelease) {
|
|
9
|
+
this.major = major;
|
|
10
|
+
this.minor = minor;
|
|
11
|
+
this.patch = patch;
|
|
12
|
+
this.prerelease = prerelease;
|
|
13
|
+
}
|
|
14
|
+
static parse(versionStr) {
|
|
15
|
+
const match = versionStr.trim().match(PromptVersion.PATTERN);
|
|
16
|
+
if (!match || !match.groups) {
|
|
17
|
+
throw new Error(`Invalid version string: '${versionStr}'`);
|
|
18
|
+
}
|
|
19
|
+
return new PromptVersion(parseInt(match.groups.major), parseInt(match.groups.minor), parseInt(match.groups.patch), match.groups.prerelease || undefined);
|
|
20
|
+
}
|
|
21
|
+
bumpPatch() {
|
|
22
|
+
return new PromptVersion(this.major, this.minor, this.patch + 1);
|
|
23
|
+
}
|
|
24
|
+
bumpMinor() {
|
|
25
|
+
return new PromptVersion(this.major, this.minor + 1, 0);
|
|
26
|
+
}
|
|
27
|
+
bumpMajor() {
|
|
28
|
+
return new PromptVersion(this.major + 1, 0, 0);
|
|
29
|
+
}
|
|
30
|
+
isPrerelease() {
|
|
31
|
+
return this.prerelease !== undefined;
|
|
32
|
+
}
|
|
33
|
+
isCompatibleWith(other) {
|
|
34
|
+
return this.major === other.major;
|
|
35
|
+
}
|
|
36
|
+
compareTo(other) {
|
|
37
|
+
if (this.major !== other.major)
|
|
38
|
+
return this.major - other.major;
|
|
39
|
+
if (this.minor !== other.minor)
|
|
40
|
+
return this.minor - other.minor;
|
|
41
|
+
return this.patch - other.patch;
|
|
42
|
+
}
|
|
43
|
+
toString() {
|
|
44
|
+
const base = `${this.major}.${this.minor}.${this.patch}`;
|
|
45
|
+
return this.prerelease ? `${base}-${this.prerelease}` : base;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
exports.PromptVersion = PromptVersion;
|
|
49
|
+
PromptVersion.PATTERN = /^(?<major>0|[1-9]\d*)\.(?<minor>0|[1-9]\d*)\.(?<patch>0|[1-9]\d*)(?:-(?<prerelease>[0-9A-Za-z\-.]+))?$/;
|
|
50
|
+
class VersionRange {
|
|
51
|
+
constructor(spec) {
|
|
52
|
+
this.spec = spec;
|
|
53
|
+
}
|
|
54
|
+
static latest() {
|
|
55
|
+
return new VersionRange("latest");
|
|
56
|
+
}
|
|
57
|
+
static exact(version) {
|
|
58
|
+
PromptVersion.parse(version);
|
|
59
|
+
return new VersionRange(version);
|
|
60
|
+
}
|
|
61
|
+
static compatible(version) {
|
|
62
|
+
return new VersionRange(`~${version}`);
|
|
63
|
+
}
|
|
64
|
+
matches(version) {
|
|
65
|
+
if (this.spec === "latest") {
|
|
66
|
+
return !version.isPrerelease();
|
|
67
|
+
}
|
|
68
|
+
if (this.spec.startsWith("~")) {
|
|
69
|
+
const base = this.spec.slice(1);
|
|
70
|
+
const parts = base.split(".");
|
|
71
|
+
const targetMajor = parseInt(parts[0]);
|
|
72
|
+
const targetMinor = parts.length > 1 ? parseInt(parts[1]) : 0;
|
|
73
|
+
return (version.major === targetMajor &&
|
|
74
|
+
version.minor >= targetMinor &&
|
|
75
|
+
!version.isPrerelease());
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
const exact = PromptVersion.parse(this.spec);
|
|
79
|
+
return (version.major === exact.major &&
|
|
80
|
+
version.minor === exact.minor &&
|
|
81
|
+
version.patch === exact.patch);
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
exports.VersionRange = VersionRange;
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "substrai-promptops",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Infrastructure-as-Code framework for prompt engineering lifecycle management",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist/**/*"
|
|
9
|
+
],
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"prepublishOnly": "npm run build"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"prompt-engineering",
|
|
16
|
+
"llm",
|
|
17
|
+
"infrastructure-as-code",
|
|
18
|
+
"versioning",
|
|
19
|
+
"testing",
|
|
20
|
+
"deployment",
|
|
21
|
+
"serverless",
|
|
22
|
+
"aws-lambda",
|
|
23
|
+
"mlops",
|
|
24
|
+
"genai",
|
|
25
|
+
"prompts",
|
|
26
|
+
"semantic-versioning",
|
|
27
|
+
"regression-testing"
|
|
28
|
+
],
|
|
29
|
+
"author": "Gaurav Singh <gaurav@substrai.dev>",
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "https://github.com/substrai/promptops"
|
|
34
|
+
},
|
|
35
|
+
"homepage": "https://github.com/substrai/promptops",
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=16.0.0"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"typescript": "^5.0.0"
|
|
41
|
+
}
|
|
42
|
+
}
|