@rcrsr/rill-ext-openai 0.9.0 → 0.11.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.
Files changed (2) hide show
  1. package/dist/index.js +207 -67
  2. package/package.json +5 -4
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // src/factory.ts
2
2
  import OpenAI from "openai";
3
3
  import {
4
- RuntimeError as RuntimeError5,
4
+ RuntimeError as RuntimeError6,
5
5
  emitExtensionEvent,
6
6
  createVector,
7
7
  isDict as isDict2,
@@ -96,6 +96,50 @@ function mapRillType(rillType) {
96
96
  }
97
97
  return jsonType;
98
98
  }
99
+ function buildPropertyFromStructuralType(rillType) {
100
+ if (rillType.type === "closure" || rillType.type === "tuple") {
101
+ throw new RuntimeError3("RILL-R004", `unsupported type for JSON Schema: ${rillType.type}`);
102
+ }
103
+ if (rillType.type === "any") {
104
+ return {};
105
+ }
106
+ if (rillType.type === "list") {
107
+ const property = { type: "array" };
108
+ if (rillType.element !== void 0) {
109
+ property.items = buildPropertyFromStructuralType(rillType.element);
110
+ }
111
+ return property;
112
+ }
113
+ if (rillType.type === "dict") {
114
+ return { type: "object" };
115
+ }
116
+ return { type: mapRillType(rillType.type) };
117
+ }
118
+ function buildJsonSchemaFromStructuralType(type, params) {
119
+ const properties = {};
120
+ const required = [];
121
+ if (type.type === "closure") {
122
+ const closureParams = type.params ?? [];
123
+ for (let i = 0; i < closureParams.length; i++) {
124
+ const [paramName, paramType] = closureParams[i];
125
+ const rillParam = params?.[i];
126
+ const property = buildPropertyFromStructuralType(paramType);
127
+ const description = rillParam?.annotations["description"];
128
+ if (typeof description === "string") {
129
+ property.description = description;
130
+ }
131
+ const enumAnnotation = rillParam?.annotations["enum"];
132
+ if (Array.isArray(enumAnnotation)) {
133
+ property.enum = enumAnnotation;
134
+ }
135
+ properties[paramName] = property;
136
+ if (rillParam === void 0 || rillParam.defaultValue === void 0) {
137
+ required.push(paramName);
138
+ }
139
+ }
140
+ }
141
+ return { type: "object", properties, required, additionalProperties: false };
142
+ }
99
143
  function buildJsonSchema(rillSchema) {
100
144
  const properties = {};
101
145
  const required = [];
@@ -300,39 +344,15 @@ async function executeToolLoop(messages, tools, maxErrors, callbacks, emitEvent,
300
344
  let inputSchema;
301
345
  const params = callable.kind === "application" ? callable.params ?? [] : callable.kind === "script" ? callable.params : [];
302
346
  if (params.length > 0) {
303
- const properties = {};
304
- const required = [];
305
- for (const param of params) {
306
- const property = {};
307
- if (param.typeName !== null) {
308
- const descriptor = {
309
- [param.name]: { type: param.typeName }
310
- };
311
- const schema = buildJsonSchema(descriptor);
312
- const built = schema.properties[param.name];
313
- if (built !== void 0) {
314
- Object.assign(property, built);
315
- }
316
- }
317
- let paramDesc;
318
- if (callable.kind === "script") {
319
- const annot = callable.paramAnnotations[param.name];
320
- paramDesc = annot?.["description"] ?? "";
321
- } else {
322
- paramDesc = param.description ?? "";
323
- }
324
- if (paramDesc) {
325
- property["description"] = paramDesc;
326
- }
327
- properties[param.name] = property;
328
- if (param.defaultValue === null) {
329
- required.push(param.name);
330
- }
331
- }
347
+ const closureType = {
348
+ type: "closure",
349
+ params: params.map((p2) => [p2.name, p2.type ?? { type: "any" }])
350
+ };
351
+ const builtSchema = buildJsonSchemaFromStructuralType(closureType, [...params]);
332
352
  inputSchema = {
333
353
  type: "object",
334
- properties,
335
- required
354
+ properties: builtSchema.properties,
355
+ required: builtSchema.required
336
356
  };
337
357
  } else {
338
358
  inputSchema = { type: "object", properties: {}, required: [] };
@@ -452,6 +472,126 @@ async function executeToolLoop(messages, tools, maxErrors, callbacks, emitEvent,
452
472
  };
453
473
  }
454
474
 
475
+ // ../../shared/ext-param/dist/param.js
476
+ import { RuntimeError as RuntimeError5 } from "@rcrsr/rill";
477
+ function validateParamName(name) {
478
+ if (name === "") {
479
+ throw new RuntimeError5("RILL-R001", "param name must not be empty");
480
+ }
481
+ if (/\s/.test(name)) {
482
+ throw new RuntimeError5("RILL-R001", "param name must be a valid identifier");
483
+ }
484
+ }
485
+ function buildAnnotations(desc) {
486
+ if (desc !== void 0) {
487
+ return { description: desc };
488
+ }
489
+ return {};
490
+ }
491
+ var p = {
492
+ /**
493
+ * IR-1: Creates a string parameter descriptor.
494
+ *
495
+ * @param name - Parameter name (must be a valid identifier)
496
+ * @param desc - Optional description
497
+ * @returns RillParam with type 'string'
498
+ */
499
+ str(name, desc) {
500
+ validateParamName(name);
501
+ return {
502
+ name,
503
+ type: { type: "string" },
504
+ defaultValue: void 0,
505
+ annotations: buildAnnotations(desc)
506
+ };
507
+ },
508
+ /**
509
+ * IR-2: Creates a number parameter descriptor.
510
+ *
511
+ * @param name - Parameter name (must be a valid identifier)
512
+ * @param desc - Optional description
513
+ * @param def - Optional default value
514
+ * @returns RillParam with type 'number'
515
+ */
516
+ num(name, desc, def) {
517
+ validateParamName(name);
518
+ return {
519
+ name,
520
+ type: { type: "number" },
521
+ defaultValue: def,
522
+ annotations: buildAnnotations(desc)
523
+ };
524
+ },
525
+ /**
526
+ * IR-3: Creates a boolean parameter descriptor.
527
+ *
528
+ * @param name - Parameter name (must be a valid identifier)
529
+ * @param desc - Optional description
530
+ * @param def - Optional default value
531
+ * @returns RillParam with type 'bool'
532
+ */
533
+ bool(name, desc, def) {
534
+ validateParamName(name);
535
+ return {
536
+ name,
537
+ type: { type: "bool" },
538
+ defaultValue: def,
539
+ annotations: buildAnnotations(desc)
540
+ };
541
+ },
542
+ /**
543
+ * IR-4: Creates a dict parameter descriptor.
544
+ *
545
+ * @param name - Parameter name (must be a valid identifier)
546
+ * @param desc - Optional description
547
+ * @param def - Optional default value
548
+ * @returns RillParam with type 'dict'
549
+ */
550
+ dict(name, desc, def) {
551
+ validateParamName(name);
552
+ return {
553
+ name,
554
+ type: { type: "dict" },
555
+ defaultValue: def,
556
+ annotations: buildAnnotations(desc)
557
+ };
558
+ },
559
+ /**
560
+ * IR-5: Creates a list parameter descriptor.
561
+ *
562
+ * @param name - Parameter name (must be a valid identifier)
563
+ * @param itemType - Optional element type; omitted when not provided
564
+ * @param desc - Optional description
565
+ * @returns RillParam with type 'list' (with element if itemType provided)
566
+ */
567
+ list(name, itemType, desc) {
568
+ validateParamName(name);
569
+ const type = itemType !== void 0 ? { type: "list", element: itemType } : { type: "list" };
570
+ return {
571
+ name,
572
+ type,
573
+ defaultValue: void 0,
574
+ annotations: buildAnnotations(desc)
575
+ };
576
+ },
577
+ /**
578
+ * IR-6: Creates a callable parameter descriptor.
579
+ *
580
+ * @param name - Parameter name (must be a valid identifier)
581
+ * @param desc - Optional description
582
+ * @returns RillParam with type 'closure'
583
+ */
584
+ callable(name, desc) {
585
+ validateParamName(name);
586
+ return {
587
+ name,
588
+ type: { type: "closure" },
589
+ defaultValue: void 0,
590
+ annotations: buildAnnotations(desc)
591
+ };
592
+ }
593
+ };
594
+
455
595
  // src/factory.ts
456
596
  var DEFAULT_MAX_COMPLETION_TOKENS = 4096;
457
597
  var detectOpenAIError = (error) => {
@@ -500,8 +640,8 @@ function createOpenAIExtension(config) {
500
640
  // IR-4: openai::message
501
641
  message: {
502
642
  params: [
503
- { name: "text", type: "string" },
504
- { name: "options", type: "dict", defaultValue: {} }
643
+ p.str("text"),
644
+ p.dict("options", void 0, {})
505
645
  ],
506
646
  fn: async (args, ctx) => {
507
647
  const startTime = Date.now();
@@ -509,7 +649,7 @@ function createOpenAIExtension(config) {
509
649
  const text = args[0];
510
650
  const options = args[1] ?? {};
511
651
  if (text.trim().length === 0) {
512
- throw new RuntimeError5("RILL-R004", "prompt text cannot be empty");
652
+ throw new RuntimeError6("RILL-R004", "prompt text cannot be empty");
513
653
  }
514
654
  const system = typeof options["system"] === "string" ? options["system"] : factorySystem;
515
655
  const maxTokens = typeof options["max_tokens"] === "number" ? options["max_tokens"] : factoryMaxTokens;
@@ -577,13 +717,13 @@ function createOpenAIExtension(config) {
577
717
  }
578
718
  },
579
719
  description: "Send single message to OpenAI API",
580
- returnType: "dict"
720
+ returnType: { type: "dict" }
581
721
  },
582
722
  // IR-5: openai::messages
583
723
  messages: {
584
724
  params: [
585
- { name: "messages", type: "list" },
586
- { name: "options", type: "dict", defaultValue: {} }
725
+ p.list("messages"),
726
+ p.dict("options", void 0, {})
587
727
  ],
588
728
  fn: async (args, ctx) => {
589
729
  const startTime = Date.now();
@@ -591,7 +731,7 @@ function createOpenAIExtension(config) {
591
731
  const messages = args[0];
592
732
  const options = args[1] ?? {};
593
733
  if (messages.length === 0) {
594
- throw new RuntimeError5(
734
+ throw new RuntimeError6(
595
735
  "RILL-R004",
596
736
  "messages list cannot be empty"
597
737
  );
@@ -608,18 +748,18 @@ function createOpenAIExtension(config) {
608
748
  for (let i = 0; i < messages.length; i++) {
609
749
  const msg = messages[i];
610
750
  if (!msg || typeof msg !== "object" || !("role" in msg)) {
611
- throw new RuntimeError5(
751
+ throw new RuntimeError6(
612
752
  "RILL-R004",
613
753
  "message missing required 'role' field"
614
754
  );
615
755
  }
616
756
  const role = msg["role"];
617
757
  if (role !== "user" && role !== "assistant" && role !== "tool") {
618
- throw new RuntimeError5("RILL-R004", `invalid role '${role}'`);
758
+ throw new RuntimeError6("RILL-R004", `invalid role '${role}'`);
619
759
  }
620
760
  if (role === "user" || role === "tool") {
621
761
  if (!("content" in msg) || typeof msg["content"] !== "string") {
622
- throw new RuntimeError5(
762
+ throw new RuntimeError6(
623
763
  "RILL-R004",
624
764
  `${role} message requires 'content'`
625
765
  );
@@ -632,7 +772,7 @@ function createOpenAIExtension(config) {
632
772
  const hasContent = "content" in msg && msg["content"];
633
773
  const hasToolCalls = "tool_calls" in msg && msg["tool_calls"];
634
774
  if (!hasContent && !hasToolCalls) {
635
- throw new RuntimeError5(
775
+ throw new RuntimeError6(
636
776
  "RILL-R004",
637
777
  "assistant message requires 'content' or 'tool_calls'"
638
778
  );
@@ -703,11 +843,11 @@ function createOpenAIExtension(config) {
703
843
  }
704
844
  },
705
845
  description: "Send multi-turn conversation to OpenAI API",
706
- returnType: "dict"
846
+ returnType: { type: "dict" }
707
847
  },
708
848
  // IR-6: openai::embed
709
849
  embed: {
710
- params: [{ name: "text", type: "string" }],
850
+ params: [p.str("text")],
711
851
  fn: async (args, ctx) => {
712
852
  const startTime = Date.now();
713
853
  try {
@@ -721,7 +861,7 @@ function createOpenAIExtension(config) {
721
861
  });
722
862
  const embeddingData = response.data[0]?.embedding;
723
863
  if (!embeddingData || embeddingData.length === 0) {
724
- throw new RuntimeError5(
864
+ throw new RuntimeError6(
725
865
  "RILL-R004",
726
866
  "OpenAI: empty embedding returned"
727
867
  );
@@ -754,11 +894,11 @@ function createOpenAIExtension(config) {
754
894
  }
755
895
  },
756
896
  description: "Generate embedding vector for text",
757
- returnType: "vector"
897
+ returnType: { type: "vector" }
758
898
  },
759
899
  // IR-7: openai::embed_batch
760
900
  embed_batch: {
761
- params: [{ name: "texts", type: "list" }],
901
+ params: [p.list("texts")],
762
902
  fn: async (args, ctx) => {
763
903
  const startTime = Date.now();
764
904
  try {
@@ -777,7 +917,7 @@ function createOpenAIExtension(config) {
777
917
  for (const embeddingItem of response.data) {
778
918
  const embeddingData = embeddingItem.embedding;
779
919
  if (!embeddingData || embeddingData.length === 0) {
780
- throw new RuntimeError5(
920
+ throw new RuntimeError6(
781
921
  "RILL-R004",
782
922
  "OpenAI: empty embedding returned"
783
923
  );
@@ -815,13 +955,13 @@ function createOpenAIExtension(config) {
815
955
  }
816
956
  },
817
957
  description: "Generate embedding vectors for multiple texts",
818
- returnType: "list"
958
+ returnType: { type: "list" }
819
959
  },
820
960
  // IR-8: openai::tool_loop
821
961
  tool_loop: {
822
962
  params: [
823
- { name: "prompt", type: "string" },
824
- { name: "options", type: "dict", defaultValue: {} }
963
+ p.str("prompt"),
964
+ p.dict("options", void 0, {})
825
965
  ],
826
966
  fn: async (args, ctx) => {
827
967
  const startTime = Date.now();
@@ -829,10 +969,10 @@ function createOpenAIExtension(config) {
829
969
  const prompt = args[0];
830
970
  const options = args[1] ?? {};
831
971
  if (prompt.trim().length === 0) {
832
- throw new RuntimeError5("RILL-R004", "prompt text cannot be empty");
972
+ throw new RuntimeError6("RILL-R004", "prompt text cannot be empty");
833
973
  }
834
974
  if (!("tools" in options) || !isDict2(options["tools"])) {
835
- throw new RuntimeError5(
975
+ throw new RuntimeError6(
836
976
  "RILL-R004",
837
977
  "tool_loop requires 'tools' option"
838
978
  );
@@ -852,17 +992,17 @@ function createOpenAIExtension(config) {
852
992
  const prependedMessages = options["messages"];
853
993
  for (const msg of prependedMessages) {
854
994
  if (!msg || typeof msg !== "object" || !("role" in msg)) {
855
- throw new RuntimeError5(
995
+ throw new RuntimeError6(
856
996
  "RILL-R004",
857
997
  "message missing required 'role' field"
858
998
  );
859
999
  }
860
1000
  const role = msg["role"];
861
1001
  if (role !== "user" && role !== "assistant") {
862
- throw new RuntimeError5("RILL-R004", `invalid role '${role}'`);
1002
+ throw new RuntimeError6("RILL-R004", `invalid role '${role}'`);
863
1003
  }
864
1004
  if (!("content" in msg) || typeof msg["content"] !== "string") {
865
- throw new RuntimeError5(
1005
+ throw new RuntimeError6(
866
1006
  "RILL-R004",
867
1007
  `${role} message requires 'content'`
868
1008
  );
@@ -1056,13 +1196,13 @@ function createOpenAIExtension(config) {
1056
1196
  }
1057
1197
  },
1058
1198
  description: "Execute tool-use loop with OpenAI API",
1059
- returnType: "dict"
1199
+ returnType: { type: "dict" }
1060
1200
  },
1061
1201
  // IR-3: openai::generate
1062
1202
  generate: {
1063
1203
  params: [
1064
- { name: "prompt", type: "string" },
1065
- { name: "options", type: "dict" }
1204
+ p.str("prompt"),
1205
+ p.dict("options")
1066
1206
  ],
1067
1207
  fn: async (args, ctx) => {
1068
1208
  const startTime = Date.now();
@@ -1070,7 +1210,7 @@ function createOpenAIExtension(config) {
1070
1210
  const prompt = args[0];
1071
1211
  const options = args[1] ?? {};
1072
1212
  if (!("schema" in options) || options["schema"] === null || options["schema"] === void 0) {
1073
- throw new RuntimeError5(
1213
+ throw new RuntimeError6(
1074
1214
  "RILL-R004",
1075
1215
  "generate requires 'schema' option"
1076
1216
  );
@@ -1090,17 +1230,17 @@ function createOpenAIExtension(config) {
1090
1230
  const prependedMessages = options["messages"];
1091
1231
  for (const msg of prependedMessages) {
1092
1232
  if (!msg || typeof msg !== "object" || !("role" in msg)) {
1093
- throw new RuntimeError5(
1233
+ throw new RuntimeError6(
1094
1234
  "RILL-R004",
1095
1235
  "message missing required 'role' field"
1096
1236
  );
1097
1237
  }
1098
1238
  const role = msg["role"];
1099
1239
  if (role !== "user" && role !== "assistant") {
1100
- throw new RuntimeError5("RILL-R004", `invalid role '${role}'`);
1240
+ throw new RuntimeError6("RILL-R004", `invalid role '${role}'`);
1101
1241
  }
1102
1242
  if (!("content" in msg) || typeof msg["content"] !== "string") {
1103
- throw new RuntimeError5(
1243
+ throw new RuntimeError6(
1104
1244
  "RILL-R004",
1105
1245
  `${role} message requires 'content'`
1106
1246
  );
@@ -1135,7 +1275,7 @@ function createOpenAIExtension(config) {
1135
1275
  data = JSON.parse(raw);
1136
1276
  } catch (parseError) {
1137
1277
  const detail = parseError instanceof Error ? parseError.message : String(parseError);
1138
- throw new RuntimeError5(
1278
+ throw new RuntimeError6(
1139
1279
  "RILL-R004",
1140
1280
  `generate: failed to parse response JSON: ${detail}`
1141
1281
  );
@@ -1164,7 +1304,7 @@ function createOpenAIExtension(config) {
1164
1304
  return result2;
1165
1305
  } catch (error) {
1166
1306
  const duration = Date.now() - startTime;
1167
- const rillError = error instanceof RuntimeError5 ? error : mapProviderError("OpenAI", error, detectOpenAIError);
1307
+ const rillError = error instanceof RuntimeError6 ? error : mapProviderError("OpenAI", error, detectOpenAIError);
1168
1308
  emitExtensionEvent(ctx, {
1169
1309
  event: "openai:error",
1170
1310
  subsystem: "extension:openai",
@@ -1175,7 +1315,7 @@ function createOpenAIExtension(config) {
1175
1315
  }
1176
1316
  },
1177
1317
  description: "Generate structured output from OpenAI API",
1178
- returnType: "dict"
1318
+ returnType: { type: "dict" }
1179
1319
  }
1180
1320
  };
1181
1321
  result.dispose = dispose;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rcrsr/rill-ext-openai",
3
- "version": "0.9.0",
3
+ "version": "0.11.0",
4
4
  "description": "rill extension for OpenAI API integration",
5
5
  "license": "MIT",
6
6
  "author": "Andre Bremer",
@@ -17,10 +17,10 @@
17
17
  "scripting"
18
18
  ],
19
19
  "peerDependencies": {
20
- "@rcrsr/rill": "^0.9.0"
20
+ "@rcrsr/rill": "^0.11.0"
21
21
  },
22
22
  "devDependencies": {
23
- "@rcrsr/rill": "^0.9.0",
23
+ "@rcrsr/rill": "^0.11.0",
24
24
  "@types/node": "^25.3.0",
25
25
  "dts-bundle-generator": "^9.5.1",
26
26
  "tsup": "^8.5.1",
@@ -43,7 +43,8 @@
43
43
  "access": "public"
44
44
  },
45
45
  "dependencies": {
46
- "openai": "^6.25.0"
46
+ "openai": "^6.25.0",
47
+ "@rcrsr/rill-ext-param-shared": "0.0.1"
47
48
  },
48
49
  "scripts": {
49
50
  "build": "tsup && dts-bundle-generator --config dts-bundle-generator.config.cjs",