sis-tools 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/src/types.ts ADDED
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Type definitions for SIS
3
+ */
4
+
5
+ export interface ToolParameter {
6
+ type: string;
7
+ description?: string;
8
+ required?: boolean;
9
+ default?: unknown;
10
+ }
11
+
12
+ export interface ToolParameters {
13
+ [key: string]: ToolParameter;
14
+ }
15
+
16
+ export interface ToolExample {
17
+ query: string;
18
+ input?: Record<string, unknown>;
19
+ output?: unknown;
20
+ }
21
+
22
+ export interface ToolMetadata {
23
+ version?: string;
24
+ author?: string;
25
+ tags?: string[];
26
+ priority?: number;
27
+ [key: string]: unknown;
28
+ }
29
+
30
+ export interface Tool {
31
+ name: string;
32
+ description: string;
33
+ parameters: ToolParameters;
34
+ returns?: Record<string, unknown>;
35
+ semanticHints: string[];
36
+ examples: ToolExample[];
37
+ handler?: ToolHandler;
38
+ metadata: ToolMetadata;
39
+ embedding?: number[];
40
+ }
41
+
42
+ export type ToolHandler = (
43
+ params: Record<string, unknown>
44
+ ) => unknown | Promise<unknown>;
45
+
46
+ export interface ToolMatch {
47
+ tool: Tool;
48
+ score: number;
49
+ }
50
+
51
+ export interface ResolvedTool {
52
+ name: string;
53
+ schema: ToolSchema;
54
+ score: number;
55
+ handler?: ToolHandler;
56
+ }
57
+
58
+ export interface ToolSchema {
59
+ name: string;
60
+ description: string;
61
+ parameters: {
62
+ type: "object";
63
+ properties: Record<string, unknown>;
64
+ required: string[];
65
+ };
66
+ }
67
+
68
+ export interface OpenAIToolSchema {
69
+ type: "function";
70
+ function: ToolSchema;
71
+ }
72
+
73
+ export interface AnthropicToolSchema {
74
+ name: string;
75
+ description: string;
76
+ input_schema: {
77
+ type: "object";
78
+ properties: Record<string, unknown>;
79
+ required: string[];
80
+ };
81
+ }
82
+
83
+ /**
84
+ * Convert a Tool to its JSON schema representation
85
+ */
86
+ export function toSchema(tool: Tool): ToolSchema {
87
+ const required = Object.entries(tool.parameters)
88
+ .filter(([, v]) => v.required !== false)
89
+ .map(([k]) => k);
90
+
91
+ return {
92
+ name: tool.name,
93
+ description: tool.description,
94
+ parameters: {
95
+ type: "object",
96
+ properties: tool.parameters,
97
+ required,
98
+ },
99
+ };
100
+ }
101
+
102
+ /**
103
+ * Convert a Tool to OpenAI function calling format
104
+ */
105
+ export function toOpenAISchema(tool: Tool): OpenAIToolSchema {
106
+ return {
107
+ type: "function",
108
+ function: toSchema(tool),
109
+ };
110
+ }
111
+
112
+ /**
113
+ * Convert a Tool to Anthropic tool use format
114
+ */
115
+ export function toAnthropicSchema(tool: Tool): AnthropicToolSchema {
116
+ const schema = toSchema(tool);
117
+ return {
118
+ name: schema.name,
119
+ description: schema.description,
120
+ input_schema: schema.parameters,
121
+ };
122
+ }
123
+
124
+ export interface RemoteToolSchema {
125
+ name: string;
126
+ schema: ToolSchema;
127
+ score: number;
128
+ }
129
+
130
+ export interface RemoteResolveResponse {
131
+ results: RemoteToolSchema[];
132
+ }
133
+
134
+ export interface RemoteListResponse {
135
+ tools: string[];
136
+ }
@@ -0,0 +1,484 @@
1
+ /**
2
+ * Validation system for SIS tools and parameters
3
+ */
4
+
5
+ import type { Tool, ToolParameters } from "./types";
6
+
7
+ /**
8
+ * Result of a validation operation
9
+ */
10
+ export interface ValidationResult {
11
+ valid: boolean;
12
+ errors: string[];
13
+ warnings: string[];
14
+ }
15
+
16
+ /**
17
+ * Create a validation result
18
+ */
19
+ export function createValidationResult(
20
+ valid: boolean,
21
+ errors: string[] = [],
22
+ warnings: string[] = []
23
+ ): ValidationResult {
24
+ return { valid, errors, warnings };
25
+ }
26
+
27
+ /**
28
+ * Merge two validation results
29
+ */
30
+ export function mergeValidationResults(
31
+ a: ValidationResult,
32
+ b: ValidationResult
33
+ ): ValidationResult {
34
+ return {
35
+ valid: a.valid && b.valid,
36
+ errors: [...a.errors, ...b.errors],
37
+ warnings: [...a.warnings, ...b.warnings],
38
+ };
39
+ }
40
+
41
+ /**
42
+ * Interface for validators
43
+ */
44
+ export interface Validator<T = unknown> {
45
+ validate(data: T): ValidationResult;
46
+ }
47
+
48
+ /**
49
+ * Validation error
50
+ */
51
+ export class ValidationError extends Error {
52
+ result: ValidationResult;
53
+
54
+ constructor(result: ValidationResult) {
55
+ super(`Validation failed: ${result.errors.join(", ")}`);
56
+ this.name = "ValidationError";
57
+ this.result = result;
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Allowed JSON Schema types
63
+ */
64
+ const ALLOWED_TYPES = new Set([
65
+ "string",
66
+ "integer",
67
+ "number",
68
+ "boolean",
69
+ "array",
70
+ "object",
71
+ "null",
72
+ ]);
73
+
74
+ /**
75
+ * Options for ToolSchemaValidator
76
+ */
77
+ export interface ToolSchemaValidatorOptions {
78
+ requireDescription?: boolean;
79
+ requireParameters?: boolean;
80
+ minDescriptionLength?: number;
81
+ maxDescriptionLength?: number;
82
+ }
83
+
84
+ /**
85
+ * Validates tool definitions
86
+ */
87
+ export class ToolSchemaValidator implements Validator<Tool> {
88
+ private options: Required<ToolSchemaValidatorOptions>;
89
+
90
+ constructor(options: ToolSchemaValidatorOptions = {}) {
91
+ this.options = {
92
+ requireDescription: options.requireDescription ?? true,
93
+ requireParameters: options.requireParameters ?? false,
94
+ minDescriptionLength: options.minDescriptionLength ?? 0,
95
+ maxDescriptionLength: options.maxDescriptionLength ?? 1000,
96
+ };
97
+ }
98
+
99
+ validate(tool: Tool): ValidationResult {
100
+ const errors: string[] = [];
101
+ const warnings: string[] = [];
102
+
103
+ // Validate name
104
+ if (!tool.name) {
105
+ errors.push("Tool name is required");
106
+ } else if (!/^[\w-]+$/.test(tool.name)) {
107
+ warnings.push(`Tool name '${tool.name}' contains special characters`);
108
+ }
109
+
110
+ // Validate description
111
+ if (this.options.requireDescription && !tool.description) {
112
+ errors.push("Tool description is required");
113
+ } else if (tool.description) {
114
+ if (tool.description.length < this.options.minDescriptionLength) {
115
+ errors.push(
116
+ `Description must be at least ${this.options.minDescriptionLength} characters`
117
+ );
118
+ }
119
+ if (tool.description.length > this.options.maxDescriptionLength) {
120
+ errors.push(
121
+ `Description must be at most ${this.options.maxDescriptionLength} characters`
122
+ );
123
+ }
124
+ }
125
+
126
+ // Validate parameters
127
+ if (this.options.requireParameters && Object.keys(tool.parameters).length === 0) {
128
+ errors.push("Tool parameters are required");
129
+ }
130
+
131
+ // Validate parameter types
132
+ for (const [name, param] of Object.entries(tool.parameters)) {
133
+ if (param.type && !ALLOWED_TYPES.has(param.type)) {
134
+ errors.push(`Parameter '${name}' has invalid type '${param.type}'`);
135
+ }
136
+ }
137
+
138
+ return createValidationResult(errors.length === 0, errors, warnings);
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Options for ParameterValidator
144
+ */
145
+ export interface ParameterValidatorOptions {
146
+ strict?: boolean;
147
+ allowExtra?: boolean;
148
+ }
149
+
150
+ /**
151
+ * Validates execution parameters against tool schema
152
+ */
153
+ export class ParameterValidator implements Validator<{ tool: Tool; params: Record<string, unknown> }> {
154
+ private options: Required<ParameterValidatorOptions>;
155
+
156
+ constructor(options: ParameterValidatorOptions = {}) {
157
+ this.options = {
158
+ strict: options.strict ?? false,
159
+ allowExtra: options.allowExtra ?? true,
160
+ };
161
+ }
162
+
163
+ validate(data: { tool: Tool; params: Record<string, unknown> }): ValidationResult {
164
+ const { tool, params } = data;
165
+ const errors: string[] = [];
166
+ const warnings: string[] = [];
167
+
168
+ if (!tool) {
169
+ errors.push("Tool is required for parameter validation");
170
+ return createValidationResult(false, errors, warnings);
171
+ }
172
+
173
+ const properties = tool.parameters;
174
+
175
+ // Check required parameters in strict mode
176
+ if (this.options.strict) {
177
+ for (const [name, param] of Object.entries(properties)) {
178
+ if (param.required !== false && !(name in params)) {
179
+ errors.push(`Missing required parameter: ${name}`);
180
+ }
181
+ }
182
+ }
183
+
184
+ // Check parameter types
185
+ for (const [name, value] of Object.entries(params)) {
186
+ if (!(name in properties)) {
187
+ if (!this.options.allowExtra) {
188
+ errors.push(`Unknown parameter: ${name}`);
189
+ } else {
190
+ warnings.push(`Extra parameter provided: ${name}`);
191
+ }
192
+ continue;
193
+ }
194
+
195
+ const param = properties[name];
196
+ const typeError = this.checkType(name, value, param.type);
197
+ if (typeError) {
198
+ errors.push(typeError);
199
+ }
200
+ }
201
+
202
+ return createValidationResult(errors.length === 0, errors, warnings);
203
+ }
204
+
205
+ private checkType(name: string, value: unknown, expectedType?: string): string | null {
206
+ if (!expectedType) return null;
207
+
208
+ const typeMap: Record<string, (v: unknown) => boolean> = {
209
+ string: (v) => typeof v === "string",
210
+ integer: (v) => Number.isInteger(v),
211
+ number: (v) => typeof v === "number",
212
+ boolean: (v) => typeof v === "boolean",
213
+ array: (v) => Array.isArray(v),
214
+ object: (v) => typeof v === "object" && v !== null && !Array.isArray(v),
215
+ null: (v) => v === null,
216
+ };
217
+
218
+ const checker = typeMap[expectedType];
219
+ if (checker && !checker(value)) {
220
+ return `Parameter '${name}' expected ${expectedType}, got ${typeof value}`;
221
+ }
222
+
223
+ return null;
224
+ }
225
+ }
226
+
227
+ /**
228
+ * Options for ResultValidator
229
+ */
230
+ export interface ResultValidatorOptions {
231
+ strict?: boolean;
232
+ }
233
+
234
+ /**
235
+ * Validates tool execution results
236
+ */
237
+ export class ResultValidator implements Validator<{ tool: Tool; result: unknown }> {
238
+ private options: Required<ResultValidatorOptions>;
239
+
240
+ constructor(options: ResultValidatorOptions = {}) {
241
+ this.options = {
242
+ strict: options.strict ?? false,
243
+ };
244
+ }
245
+
246
+ validate(data: { tool: Tool; result: unknown }): ValidationResult {
247
+ const { tool, result } = data;
248
+ const errors: string[] = [];
249
+ const warnings: string[] = [];
250
+
251
+ if (!tool) {
252
+ errors.push("Tool is required for result validation");
253
+ return createValidationResult(false, errors, warnings);
254
+ }
255
+
256
+ // If tool has return schema, validate against it
257
+ if (tool.returns) {
258
+ const expectedType = tool.returns.type as string;
259
+ const typeError = this.checkReturnType(result, expectedType);
260
+ if (typeError) {
261
+ if (this.options.strict) {
262
+ errors.push(typeError);
263
+ } else {
264
+ warnings.push(typeError);
265
+ }
266
+ }
267
+ }
268
+
269
+ return createValidationResult(errors.length === 0, errors, warnings);
270
+ }
271
+
272
+ private checkReturnType(value: unknown, expectedType?: string): string | null {
273
+ if (!expectedType) return null;
274
+
275
+ const typeMap: Record<string, (v: unknown) => boolean> = {
276
+ string: (v) => typeof v === "string",
277
+ integer: (v) => Number.isInteger(v),
278
+ number: (v) => typeof v === "number",
279
+ boolean: (v) => typeof v === "boolean",
280
+ array: (v) => Array.isArray(v),
281
+ object: (v) => typeof v === "object" && v !== null && !Array.isArray(v),
282
+ null: (v) => v === null,
283
+ };
284
+
285
+ const checker = typeMap[expectedType];
286
+ if (checker && !checker(value)) {
287
+ return `Result expected ${expectedType}, got ${typeof value}`;
288
+ }
289
+
290
+ return null;
291
+ }
292
+ }
293
+
294
+ /**
295
+ * Options for EmbeddingValidator
296
+ */
297
+ export interface EmbeddingValidatorOptions {
298
+ expectedDimensions?: number;
299
+ checkNormalization?: boolean;
300
+ }
301
+
302
+ /**
303
+ * Validates embedding vectors
304
+ */
305
+ export class EmbeddingValidator implements Validator<number[]> {
306
+ private options: EmbeddingValidatorOptions;
307
+
308
+ constructor(options: EmbeddingValidatorOptions = {}) {
309
+ this.options = options;
310
+ }
311
+
312
+ validate(embedding: number[]): ValidationResult {
313
+ const errors: string[] = [];
314
+ const warnings: string[] = [];
315
+
316
+ if (!Array.isArray(embedding)) {
317
+ errors.push("Embedding must be an array");
318
+ return createValidationResult(false, errors, warnings);
319
+ }
320
+
321
+ if (embedding.length === 0) {
322
+ errors.push("Embedding cannot be empty");
323
+ return createValidationResult(false, errors, warnings);
324
+ }
325
+
326
+ // Check dimensions
327
+ if (
328
+ this.options.expectedDimensions !== undefined &&
329
+ embedding.length !== this.options.expectedDimensions
330
+ ) {
331
+ errors.push(
332
+ `Embedding dimensions mismatch: expected ${this.options.expectedDimensions}, got ${embedding.length}`
333
+ );
334
+ }
335
+
336
+ // Check for non-numeric values
337
+ for (let i = 0; i < embedding.length; i++) {
338
+ if (typeof embedding[i] !== "number") {
339
+ errors.push(`Embedding value at index ${i} is not numeric`);
340
+ break;
341
+ }
342
+ }
343
+
344
+ // Check normalization
345
+ if (this.options.checkNormalization && errors.length === 0) {
346
+ let sumSquares = 0;
347
+ for (const val of embedding) {
348
+ sumSquares += val * val;
349
+ }
350
+ const norm = Math.sqrt(sumSquares);
351
+ if (Math.abs(norm - 1.0) > 0.01) {
352
+ warnings.push(`Embedding is not normalized (norm=${norm.toFixed(4)})`);
353
+ }
354
+ }
355
+
356
+ return createValidationResult(errors.length === 0, errors, warnings);
357
+ }
358
+ }
359
+
360
+ /**
361
+ * Combines multiple validators
362
+ */
363
+ export class CompositeValidator<T> implements Validator<T> {
364
+ private validators: Validator<T>[];
365
+
366
+ constructor(validators: Validator<T>[]) {
367
+ this.validators = validators;
368
+ }
369
+
370
+ validate(data: T): ValidationResult {
371
+ let result = createValidationResult(true);
372
+ for (const validator of this.validators) {
373
+ const subResult = validator.validate(data);
374
+ result = mergeValidationResults(result, subResult);
375
+ }
376
+ return result;
377
+ }
378
+ }
379
+
380
+ /**
381
+ * Registry for managing validators
382
+ */
383
+ export class ValidatorRegistry {
384
+ schemaValidators: ToolSchemaValidator[] = [];
385
+ parameterValidators: ParameterValidator[] = [];
386
+ resultValidators: ResultValidator[] = [];
387
+ embeddingValidators: EmbeddingValidator[] = [];
388
+
389
+ addSchemaValidator(validator: ToolSchemaValidator): void {
390
+ this.schemaValidators.push(validator);
391
+ }
392
+
393
+ addParameterValidator(validator: ParameterValidator): void {
394
+ this.parameterValidators.push(validator);
395
+ }
396
+
397
+ addResultValidator(validator: ResultValidator): void {
398
+ this.resultValidators.push(validator);
399
+ }
400
+
401
+ addEmbeddingValidator(validator: EmbeddingValidator): void {
402
+ this.embeddingValidators.push(validator);
403
+ }
404
+
405
+ validateTool(tool: Tool): ValidationResult {
406
+ let result = createValidationResult(true);
407
+ for (const validator of this.schemaValidators) {
408
+ const subResult = validator.validate(tool);
409
+ result = mergeValidationResults(result, subResult);
410
+ }
411
+ return result;
412
+ }
413
+
414
+ validateParams(tool: Tool, params: Record<string, unknown>): ValidationResult {
415
+ let result = createValidationResult(true);
416
+ for (const validator of this.parameterValidators) {
417
+ const subResult = validator.validate({ tool, params });
418
+ result = mergeValidationResults(result, subResult);
419
+ }
420
+ return result;
421
+ }
422
+
423
+ validateResult(tool: Tool, resultValue: unknown): ValidationResult {
424
+ let result = createValidationResult(true);
425
+ for (const validator of this.resultValidators) {
426
+ const subResult = validator.validate({ tool, result: resultValue });
427
+ result = mergeValidationResults(result, subResult);
428
+ }
429
+ return result;
430
+ }
431
+
432
+ validateEmbedding(embedding: number[]): ValidationResult {
433
+ let result = createValidationResult(true);
434
+ for (const validator of this.embeddingValidators) {
435
+ const subResult = validator.validate(embedding);
436
+ result = mergeValidationResults(result, subResult);
437
+ }
438
+ return result;
439
+ }
440
+
441
+ clear(): void {
442
+ this.schemaValidators = [];
443
+ this.parameterValidators = [];
444
+ this.resultValidators = [];
445
+ this.embeddingValidators = [];
446
+ }
447
+ }
448
+
449
+ /**
450
+ * Create a registry with strict validators
451
+ */
452
+ export function createStrictValidator(): ValidatorRegistry {
453
+ const registry = new ValidatorRegistry();
454
+ registry.addSchemaValidator(
455
+ new ToolSchemaValidator({
456
+ requireDescription: true,
457
+ minDescriptionLength: 10,
458
+ })
459
+ );
460
+ registry.addParameterValidator(new ParameterValidator({ strict: true }));
461
+ registry.addResultValidator(new ResultValidator({ strict: true }));
462
+ return registry;
463
+ }
464
+
465
+ /**
466
+ * Create a registry with lenient validators
467
+ */
468
+ export function createLenientValidator(): ValidatorRegistry {
469
+ const registry = new ValidatorRegistry();
470
+ registry.addSchemaValidator(
471
+ new ToolSchemaValidator({
472
+ requireDescription: false,
473
+ minDescriptionLength: 0,
474
+ })
475
+ );
476
+ registry.addParameterValidator(
477
+ new ParameterValidator({
478
+ strict: false,
479
+ allowExtra: true,
480
+ })
481
+ );
482
+ registry.addResultValidator(new ResultValidator({ strict: false }));
483
+ return registry;
484
+ }