@rcrsr/rill-ext-openai 0.18.4 → 0.19.1
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/index.js +305 -116
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -4,11 +4,13 @@ import { createRequire } from "module";
|
|
|
4
4
|
// src/factory.ts
|
|
5
5
|
import OpenAI from "openai";
|
|
6
6
|
import {
|
|
7
|
-
RuntimeError as
|
|
7
|
+
RuntimeError as RuntimeError5,
|
|
8
|
+
RuntimeHaltSignal as RuntimeHaltSignal3,
|
|
8
9
|
createRillStream,
|
|
9
10
|
emitExtensionEvent,
|
|
10
11
|
createVector,
|
|
11
12
|
isVector,
|
|
13
|
+
getStatus as getStatus2,
|
|
12
14
|
structureToTypeValue,
|
|
13
15
|
toCallable
|
|
14
16
|
} from "@rcrsr/rill";
|
|
@@ -64,27 +66,137 @@ function validateEmbedModel(model) {
|
|
|
64
66
|
}
|
|
65
67
|
|
|
66
68
|
// ../../shared/ext-llm/dist/errors.js
|
|
67
|
-
import {
|
|
68
|
-
function
|
|
69
|
+
import { RuntimeHaltSignal } from "@rcrsr/rill";
|
|
70
|
+
function atomForStatus(status) {
|
|
71
|
+
if (status === 401)
|
|
72
|
+
return "AUTH";
|
|
73
|
+
if (status === 403)
|
|
74
|
+
return "FORBIDDEN";
|
|
75
|
+
if (status === 404)
|
|
76
|
+
return "NOT_FOUND";
|
|
77
|
+
if (status === 408)
|
|
78
|
+
return "TIMEOUT";
|
|
79
|
+
if (status === 409 || status === 412)
|
|
80
|
+
return "CONFLICT";
|
|
81
|
+
if (status === 429)
|
|
82
|
+
return "RATE_LIMIT";
|
|
83
|
+
if (status === 402)
|
|
84
|
+
return "QUOTA_EXCEEDED";
|
|
85
|
+
if (status >= 500 && status <= 599)
|
|
86
|
+
return "UNAVAILABLE";
|
|
87
|
+
if (status >= 400 && status <= 499)
|
|
88
|
+
return "INVALID_INPUT";
|
|
89
|
+
return "UNAVAILABLE";
|
|
90
|
+
}
|
|
91
|
+
function kindForStatus(status) {
|
|
92
|
+
if (status === 401)
|
|
93
|
+
return "authentication_failed";
|
|
94
|
+
if (status === 403)
|
|
95
|
+
return "forbidden";
|
|
96
|
+
if (status === 404)
|
|
97
|
+
return "not_found";
|
|
98
|
+
if (status === 408)
|
|
99
|
+
return "request_timeout";
|
|
100
|
+
if (status === 409 || status === 412)
|
|
101
|
+
return "conflict";
|
|
102
|
+
if (status === 429)
|
|
103
|
+
return "rate_limit_exceeded";
|
|
104
|
+
if (status === 402)
|
|
105
|
+
return "quota_exceeded";
|
|
106
|
+
if (status >= 500 && status <= 599)
|
|
107
|
+
return "server_error";
|
|
108
|
+
return "http_error";
|
|
109
|
+
}
|
|
110
|
+
function mapProviderError(ctx, provider, error, detect) {
|
|
111
|
+
if (error instanceof RuntimeHaltSignal) {
|
|
112
|
+
return ctx.invalidate(error, {
|
|
113
|
+
code: "TIMEOUT",
|
|
114
|
+
provider,
|
|
115
|
+
raw: { kind: "request_cancelled", message: `${provider}: request cancelled` }
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
119
|
+
return ctx.invalidate(error, {
|
|
120
|
+
code: "TIMEOUT",
|
|
121
|
+
provider,
|
|
122
|
+
raw: {
|
|
123
|
+
kind: "request_timeout",
|
|
124
|
+
message: `${provider} error: ${error.message}`
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
}
|
|
69
128
|
const detected = detect(error);
|
|
70
129
|
if (detected !== null) {
|
|
71
130
|
const { status, message } = detected;
|
|
72
131
|
if (status !== void 0) {
|
|
73
|
-
return
|
|
132
|
+
return ctx.invalidate(error, {
|
|
133
|
+
code: atomForStatus(status),
|
|
134
|
+
provider,
|
|
135
|
+
raw: {
|
|
136
|
+
kind: kindForStatus(status),
|
|
137
|
+
status,
|
|
138
|
+
message: `${provider} API error (HTTP ${status}): ${message}`
|
|
139
|
+
}
|
|
140
|
+
});
|
|
74
141
|
}
|
|
75
|
-
return
|
|
142
|
+
return ctx.invalidate(error, {
|
|
143
|
+
code: "UNAVAILABLE",
|
|
144
|
+
provider,
|
|
145
|
+
raw: {
|
|
146
|
+
kind: "provider_error",
|
|
147
|
+
message: `${provider} API error: ${message}`
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
if (error instanceof TypeError) {
|
|
152
|
+
return ctx.invalidate(error, {
|
|
153
|
+
code: "UNAVAILABLE",
|
|
154
|
+
provider,
|
|
155
|
+
raw: {
|
|
156
|
+
kind: "connection_failed",
|
|
157
|
+
message: `${provider} error: ${error.message}`
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
if (error instanceof SyntaxError) {
|
|
162
|
+
return ctx.invalidate(error, {
|
|
163
|
+
code: "PROTOCOL",
|
|
164
|
+
provider,
|
|
165
|
+
raw: {
|
|
166
|
+
kind: "unexpected_response_format",
|
|
167
|
+
message: `${provider} error: ${error.message}`
|
|
168
|
+
}
|
|
169
|
+
});
|
|
76
170
|
}
|
|
77
171
|
if (error instanceof Error) {
|
|
78
|
-
return
|
|
172
|
+
return ctx.invalidate(error, {
|
|
173
|
+
code: "UNAVAILABLE",
|
|
174
|
+
provider,
|
|
175
|
+
raw: {
|
|
176
|
+
kind: "unknown_error",
|
|
177
|
+
message: `${provider} error: ${error.message}`
|
|
178
|
+
}
|
|
179
|
+
});
|
|
79
180
|
}
|
|
80
|
-
return
|
|
181
|
+
return ctx.invalidate(error, {
|
|
182
|
+
code: "UNAVAILABLE",
|
|
183
|
+
provider,
|
|
184
|
+
raw: {
|
|
185
|
+
kind: "unknown_error",
|
|
186
|
+
message: `${provider} error: Unknown error`
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
function throwProviderHalt(ctx, provider, error, detect) {
|
|
191
|
+
const invalid = mapProviderError(ctx, provider, error, detect);
|
|
192
|
+
throw new RuntimeHaltSignal(invalid, true);
|
|
81
193
|
}
|
|
82
194
|
|
|
83
195
|
// ../../shared/ext-llm/dist/tool-loop.js
|
|
84
|
-
import { invokeCallable, isCallable, isDict, isRuntimeCallable, RuntimeError as
|
|
196
|
+
import { getStatus, invokeCallable, isCallable, isDict, isRuntimeCallable, RuntimeError as RuntimeError3, RuntimeHaltSignal as RuntimeHaltSignal2 } from "@rcrsr/rill";
|
|
85
197
|
|
|
86
198
|
// ../../shared/ext-llm/dist/schema.js
|
|
87
|
-
import { RuntimeError as
|
|
199
|
+
import { RuntimeError as RuntimeError2 } from "@rcrsr/rill";
|
|
88
200
|
var RILL_TYPE_MAP = {
|
|
89
201
|
string: "string",
|
|
90
202
|
number: "number",
|
|
@@ -97,24 +209,24 @@ var RILL_TYPE_MAP = {
|
|
|
97
209
|
function mapRillType(rillType) {
|
|
98
210
|
const jsonType = RILL_TYPE_MAP[rillType];
|
|
99
211
|
if (jsonType === void 0) {
|
|
100
|
-
throw new
|
|
212
|
+
throw new RuntimeError2("RILL-R005", `unsupported type: ${rillType}`);
|
|
101
213
|
}
|
|
102
214
|
return jsonType;
|
|
103
215
|
}
|
|
104
216
|
function buildPropertyFromStructuralType(rillType) {
|
|
105
217
|
if (rillType.kind === "closure" || rillType.kind === "tuple") {
|
|
106
|
-
throw new
|
|
218
|
+
throw new RuntimeError2("RILL-R005", `unsupported type for JSON Schema: ${rillType.kind}`);
|
|
107
219
|
}
|
|
108
220
|
if (rillType.kind === "any") {
|
|
109
221
|
return {};
|
|
110
222
|
}
|
|
111
223
|
if (rillType.kind === "list") {
|
|
112
224
|
const listType = rillType;
|
|
113
|
-
const
|
|
225
|
+
const property2 = { type: "array" };
|
|
114
226
|
if (listType.element !== void 0) {
|
|
115
|
-
|
|
227
|
+
property2.items = buildPropertyFromStructuralType(listType.element);
|
|
116
228
|
}
|
|
117
|
-
return
|
|
229
|
+
return property2;
|
|
118
230
|
}
|
|
119
231
|
if (rillType.kind === "dict") {
|
|
120
232
|
const dictType = rillType;
|
|
@@ -127,9 +239,14 @@ function buildPropertyFromStructuralType(rillType) {
|
|
|
127
239
|
additionalProperties: false
|
|
128
240
|
};
|
|
129
241
|
}
|
|
130
|
-
return { type: "object" };
|
|
242
|
+
return { type: "object", additionalProperties: false };
|
|
243
|
+
}
|
|
244
|
+
const jsonType = mapRillType(rillType.kind);
|
|
245
|
+
const property = { type: jsonType };
|
|
246
|
+
if (jsonType === "object") {
|
|
247
|
+
property.additionalProperties = false;
|
|
131
248
|
}
|
|
132
|
-
return
|
|
249
|
+
return property;
|
|
133
250
|
}
|
|
134
251
|
function buildDictSchema(dictType) {
|
|
135
252
|
const properties = {};
|
|
@@ -153,7 +270,7 @@ function buildJsonSchemaFromStructuralType(type, params) {
|
|
|
153
270
|
return buildDictSchema(type);
|
|
154
271
|
}
|
|
155
272
|
if (type.kind !== "closure") {
|
|
156
|
-
throw new
|
|
273
|
+
throw new RuntimeError2("RILL-R005", `unsupported schema kind: ${type.kind} (expected dict or closure)`);
|
|
157
274
|
}
|
|
158
275
|
const properties = {};
|
|
159
276
|
const required = [];
|
|
@@ -181,30 +298,51 @@ function buildJsonSchemaFromStructuralType(type, params) {
|
|
|
181
298
|
}
|
|
182
299
|
|
|
183
300
|
// ../../shared/ext-llm/dist/tool-loop.js
|
|
301
|
+
function readHaltMessage(halt) {
|
|
302
|
+
const status = getStatus(halt.value);
|
|
303
|
+
if (typeof status.message === "string" && status.message.length > 0) {
|
|
304
|
+
return status.message;
|
|
305
|
+
}
|
|
306
|
+
return halt.message;
|
|
307
|
+
}
|
|
308
|
+
function throwToolLoopHalt(ctx, code, kind, message) {
|
|
309
|
+
if (ctx !== void 0) {
|
|
310
|
+
const hostCtx = ctx;
|
|
311
|
+
if (typeof hostCtx.invalidate === "function") {
|
|
312
|
+
const invalid = hostCtx.invalidate(new Error(message), {
|
|
313
|
+
code,
|
|
314
|
+
provider: "tool_loop",
|
|
315
|
+
raw: { kind, message }
|
|
316
|
+
});
|
|
317
|
+
throw new RuntimeHaltSignal2(invalid, true);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
throw new RuntimeError3("RILL-R005", message);
|
|
321
|
+
}
|
|
184
322
|
async function executeToolCall(toolName, toolInput, tools, context) {
|
|
185
323
|
if (!isDict(tools)) {
|
|
186
|
-
|
|
324
|
+
throwToolLoopHalt(context, "INVALID_INPUT", "tools_not_dict", "tool_loop: tools must be a dict of name \u2192 callable");
|
|
187
325
|
}
|
|
188
326
|
const toolsDict = tools;
|
|
189
327
|
const toolFn = toolsDict[toolName];
|
|
190
328
|
if (toolFn === void 0 || toolFn === null) {
|
|
191
|
-
|
|
329
|
+
throwToolLoopHalt(context, "NOT_FOUND", "unknown_tool", `Unknown tool: ${toolName}`);
|
|
192
330
|
}
|
|
193
331
|
if (!isCallable(toolFn)) {
|
|
194
|
-
|
|
332
|
+
throwToolLoopHalt(context, "INVALID_INPUT", "tool_not_callable", `Invalid tool input for ${toolName}: tool must be callable`);
|
|
195
333
|
}
|
|
196
334
|
if (typeof toolInput !== "object" || toolInput === null) {
|
|
197
|
-
|
|
335
|
+
throwToolLoopHalt(context, "INVALID_INPUT", "invalid_tool_input", `Invalid tool input for ${toolName}: input must be an object`);
|
|
198
336
|
}
|
|
199
337
|
const callable = toolFn;
|
|
200
338
|
if (callable.kind !== "runtime" && callable.kind !== "application" && callable.kind !== "script") {
|
|
201
|
-
|
|
339
|
+
throwToolLoopHalt(context, "INVALID_INPUT", "tool_kind_unsupported", `Invalid tool input for ${toolName}: tool must be application, runtime, or script callable`);
|
|
202
340
|
}
|
|
203
341
|
try {
|
|
204
342
|
const inputDict = toolInput;
|
|
205
343
|
if (callable.kind === "script") {
|
|
206
344
|
if (!context) {
|
|
207
|
-
throw new
|
|
345
|
+
throw new RuntimeError3("RILL-R005", `Invalid tool input for ${toolName}: script callable requires a runtime context`);
|
|
208
346
|
}
|
|
209
347
|
let args;
|
|
210
348
|
if (callable.params && callable.params.length > 0) {
|
|
@@ -220,16 +358,17 @@ async function executeToolCall(toolName, toolInput, tools, context) {
|
|
|
220
358
|
const ctx = context ?? {
|
|
221
359
|
parent: void 0,
|
|
222
360
|
variables: /* @__PURE__ */ new Map(),
|
|
223
|
-
pipeValue: null
|
|
361
|
+
pipeValue: null,
|
|
362
|
+
hostContext: {}
|
|
224
363
|
};
|
|
225
364
|
const result = callable.fn(inputDict, ctx);
|
|
226
365
|
return result instanceof Promise ? await result : result;
|
|
227
366
|
} catch (error) {
|
|
228
|
-
if (error instanceof
|
|
367
|
+
if (error instanceof RuntimeError3) {
|
|
229
368
|
throw error;
|
|
230
369
|
}
|
|
231
370
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
232
|
-
throw new
|
|
371
|
+
throw new RuntimeError3("RILL-R005", `Invalid tool input for ${toolName}: ${message}`);
|
|
233
372
|
}
|
|
234
373
|
}
|
|
235
374
|
function sanitizeToolName(name) {
|
|
@@ -300,19 +439,19 @@ function patchResponseToolCallNames(response, nameMap) {
|
|
|
300
439
|
}
|
|
301
440
|
async function executeToolLoop(messages, tools, maxErrors, callbacks, emitEvent, maxTurns = 10, context, yieldChunk, signal) {
|
|
302
441
|
if (tools === void 0) {
|
|
303
|
-
|
|
442
|
+
throwToolLoopHalt(context, "INVALID_INPUT", "tools_required", "tools parameter is required");
|
|
304
443
|
}
|
|
305
444
|
if (!isDict(tools)) {
|
|
306
|
-
|
|
445
|
+
throwToolLoopHalt(context, "INVALID_INPUT", "tools_not_dict", "tool_loop: tools must be a dict of name \u2192 callable");
|
|
307
446
|
}
|
|
308
447
|
const toolsDict = tools;
|
|
309
448
|
const toolDescriptors = Object.entries(toolsDict).map(([name, fn]) => {
|
|
310
449
|
const fnValue = fn;
|
|
311
450
|
if (isRuntimeCallable(fnValue)) {
|
|
312
|
-
|
|
451
|
+
throwToolLoopHalt(context, "INVALID_INPUT", "builtin_tool_unsupported", `tool_loop: builtin "${name}" cannot be used as a tool \u2014 wrap in a closure`);
|
|
313
452
|
}
|
|
314
453
|
if (!isCallable(fnValue)) {
|
|
315
|
-
|
|
454
|
+
throwToolLoopHalt(context, "INVALID_INPUT", "tool_not_callable", `tool_loop: tool "${name}" is not a callable`);
|
|
316
455
|
}
|
|
317
456
|
const callable = fnValue;
|
|
318
457
|
const description = callable.annotations?.["description"] ?? "";
|
|
@@ -347,7 +486,7 @@ async function executeToolLoop(messages, tools, maxErrors, callbacks, emitEvent,
|
|
|
347
486
|
let turnCount = 0;
|
|
348
487
|
while (turnCount < maxTurns) {
|
|
349
488
|
if (signal?.aborted) {
|
|
350
|
-
|
|
489
|
+
throwToolLoopHalt(context, "TIMEOUT", "tool_loop_cancelled", "tool_loop cancelled");
|
|
351
490
|
}
|
|
352
491
|
turnCount++;
|
|
353
492
|
let response;
|
|
@@ -361,8 +500,18 @@ async function executeToolLoop(messages, tools, maxErrors, callbacks, emitEvent,
|
|
|
361
500
|
response = await callbacks.callAPI(currentMessages, providerTools, signal);
|
|
362
501
|
}
|
|
363
502
|
} catch (error) {
|
|
503
|
+
if (error instanceof RuntimeHaltSignal2) {
|
|
504
|
+
throw error;
|
|
505
|
+
}
|
|
506
|
+
if (context !== void 0 && callbacks.detectError !== void 0) {
|
|
507
|
+
const hostCtx = context;
|
|
508
|
+
if (typeof hostCtx.invalidate === "function") {
|
|
509
|
+
const invalid = mapProviderError(hostCtx, "tool_loop", error, callbacks.detectError);
|
|
510
|
+
throw new RuntimeHaltSignal2(invalid, true);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
364
513
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
365
|
-
throw new
|
|
514
|
+
throw new RuntimeError3("RILL-R005", `Provider API error: ${message}`, void 0, { cause: error });
|
|
366
515
|
}
|
|
367
516
|
if (typeof response === "object" && response !== null && "usage" in response) {
|
|
368
517
|
const usage = response["usage"];
|
|
@@ -419,7 +568,11 @@ async function executeToolLoop(messages, tools, maxErrors, callbacks, emitEvent,
|
|
|
419
568
|
const duration = Date.now() - toolStartTime;
|
|
420
569
|
consecutiveErrors++;
|
|
421
570
|
let originalError;
|
|
422
|
-
if (error instanceof
|
|
571
|
+
if (error instanceof RuntimeHaltSignal2) {
|
|
572
|
+
const haltMessage = readHaltMessage(error);
|
|
573
|
+
const prefix = `Invalid tool input for ${name}: `;
|
|
574
|
+
originalError = haltMessage.startsWith(prefix) ? haltMessage.slice(prefix.length) : haltMessage;
|
|
575
|
+
} else if (error instanceof RuntimeError3) {
|
|
423
576
|
const prefix = `Invalid tool input for ${name}: `;
|
|
424
577
|
if (error.message.startsWith(prefix)) {
|
|
425
578
|
originalError = error.message.slice(prefix.length);
|
|
@@ -444,7 +597,7 @@ async function executeToolLoop(messages, tools, maxErrors, callbacks, emitEvent,
|
|
|
444
597
|
duration
|
|
445
598
|
});
|
|
446
599
|
if (consecutiveErrors >= maxErrors) {
|
|
447
|
-
|
|
600
|
+
throwToolLoopHalt(context, "UNAVAILABLE", "consecutive_tool_errors", `Tool execution failed: ${maxErrors} consecutive errors (last: ${name}: ${originalError})`);
|
|
448
601
|
}
|
|
449
602
|
}
|
|
450
603
|
}
|
|
@@ -471,13 +624,13 @@ function buildResponseMessages(inputMessages, assistantContent) {
|
|
|
471
624
|
}
|
|
472
625
|
|
|
473
626
|
// ../../shared/ext-param/dist/param.js
|
|
474
|
-
import { RuntimeError as
|
|
627
|
+
import { RuntimeError as RuntimeError4 } from "@rcrsr/rill";
|
|
475
628
|
function validateParamName(name) {
|
|
476
629
|
if (name === "") {
|
|
477
|
-
throw new
|
|
630
|
+
throw new RuntimeError4("RILL-R001", "param name must not be empty");
|
|
478
631
|
}
|
|
479
632
|
if (/\s/.test(name)) {
|
|
480
|
-
throw new
|
|
633
|
+
throw new RuntimeError4("RILL-R001", "param name must be a valid identifier");
|
|
481
634
|
}
|
|
482
635
|
}
|
|
483
636
|
function buildAnnotations(desc) {
|
|
@@ -603,6 +756,16 @@ var detectOpenAIError = (error) => {
|
|
|
603
756
|
}
|
|
604
757
|
return null;
|
|
605
758
|
};
|
|
759
|
+
function haltInvalid(ctx, code, rawKind, message) {
|
|
760
|
+
return new RuntimeHaltSignal3(
|
|
761
|
+
ctx.invalidate(new Error(message), {
|
|
762
|
+
code,
|
|
763
|
+
provider: "openai",
|
|
764
|
+
raw: { kind: rawKind, message }
|
|
765
|
+
}),
|
|
766
|
+
true
|
|
767
|
+
);
|
|
768
|
+
}
|
|
606
769
|
function createOpenAIExtension(config) {
|
|
607
770
|
validateApiKey(config.api_key);
|
|
608
771
|
validateModel(config.model);
|
|
@@ -650,7 +813,7 @@ function createOpenAIExtension(config) {
|
|
|
650
813
|
const text = args["text"];
|
|
651
814
|
const options = args["options"] ?? {};
|
|
652
815
|
if (text.trim().length === 0) {
|
|
653
|
-
throw
|
|
816
|
+
throw haltInvalid(ctx, "INVALID_INPUT", "empty_prompt", "prompt text cannot be empty");
|
|
654
817
|
}
|
|
655
818
|
const system = typeof options["system"] === "string" ? options["system"] : factorySystem;
|
|
656
819
|
const maxTokens = typeof options["max_tokens"] === "number" && options["max_tokens"] > 0 ? options["max_tokens"] : factoryMaxTokens;
|
|
@@ -681,7 +844,7 @@ function createOpenAIExtension(config) {
|
|
|
681
844
|
}
|
|
682
845
|
}
|
|
683
846
|
} catch (error) {
|
|
684
|
-
|
|
847
|
+
throwProviderHalt(ctx, "OpenAI", error, detectOpenAIError);
|
|
685
848
|
}
|
|
686
849
|
}
|
|
687
850
|
const resolve = async () => {
|
|
@@ -719,14 +882,23 @@ function createOpenAIExtension(config) {
|
|
|
719
882
|
return result;
|
|
720
883
|
} catch (error) {
|
|
721
884
|
const duration = Date.now() - startTime;
|
|
722
|
-
|
|
885
|
+
if (error instanceof RuntimeHaltSignal3) {
|
|
886
|
+
emitExtensionEvent(ctx, {
|
|
887
|
+
event: "openai:error",
|
|
888
|
+
subsystem: "extension:openai",
|
|
889
|
+
error: getStatus2(error.value).message,
|
|
890
|
+
duration
|
|
891
|
+
});
|
|
892
|
+
throw error;
|
|
893
|
+
}
|
|
894
|
+
const invalid = mapProviderError(ctx, "OpenAI", error, detectOpenAIError);
|
|
723
895
|
emitExtensionEvent(ctx, {
|
|
724
896
|
event: "openai:error",
|
|
725
897
|
subsystem: "extension:openai",
|
|
726
|
-
error:
|
|
898
|
+
error: getStatus2(invalid).message,
|
|
727
899
|
duration
|
|
728
900
|
});
|
|
729
|
-
throw
|
|
901
|
+
throw new RuntimeHaltSignal3(invalid, true);
|
|
730
902
|
}
|
|
731
903
|
};
|
|
732
904
|
const retType = {
|
|
@@ -780,10 +952,7 @@ function createOpenAIExtension(config) {
|
|
|
780
952
|
const messages = args["messages"];
|
|
781
953
|
const options = args["options"] ?? {};
|
|
782
954
|
if (messages.length === 0) {
|
|
783
|
-
throw
|
|
784
|
-
"RILL-R004",
|
|
785
|
-
"messages list cannot be empty"
|
|
786
|
-
);
|
|
955
|
+
throw haltInvalid(ctx, "INVALID_INPUT", "empty_messages", "messages list cannot be empty");
|
|
787
956
|
}
|
|
788
957
|
const system = typeof options["system"] === "string" ? options["system"] : factorySystem;
|
|
789
958
|
const maxTokens = typeof options["max_tokens"] === "number" && options["max_tokens"] > 0 ? options["max_tokens"] : factoryMaxTokens;
|
|
@@ -797,21 +966,15 @@ function createOpenAIExtension(config) {
|
|
|
797
966
|
for (let i = 0; i < messages.length; i++) {
|
|
798
967
|
const msg = messages[i];
|
|
799
968
|
if (!msg || typeof msg !== "object" || !("role" in msg)) {
|
|
800
|
-
throw
|
|
801
|
-
"RILL-R004",
|
|
802
|
-
"message missing required 'role' field"
|
|
803
|
-
);
|
|
969
|
+
throw haltInvalid(ctx, "INVALID_INPUT", "invalid_message_format", "message missing required 'role' field");
|
|
804
970
|
}
|
|
805
971
|
const role = msg["role"];
|
|
806
972
|
if (role !== "user" && role !== "assistant" && role !== "tool") {
|
|
807
|
-
throw
|
|
973
|
+
throw haltInvalid(ctx, "INVALID_INPUT", "invalid_role", `invalid role '${String(role)}'`);
|
|
808
974
|
}
|
|
809
975
|
if (role === "user" || role === "tool") {
|
|
810
976
|
if (!("content" in msg) || typeof msg["content"] !== "string") {
|
|
811
|
-
throw
|
|
812
|
-
"RILL-R004",
|
|
813
|
-
`${role} message requires 'content'`
|
|
814
|
-
);
|
|
977
|
+
throw haltInvalid(ctx, "INVALID_INPUT", "missing_message_content", `${role} message requires 'content'`);
|
|
815
978
|
}
|
|
816
979
|
apiMessages.push({
|
|
817
980
|
role,
|
|
@@ -821,10 +984,7 @@ function createOpenAIExtension(config) {
|
|
|
821
984
|
const hasContent = "content" in msg && msg["content"];
|
|
822
985
|
const hasToolCalls = "tool_calls" in msg && msg["tool_calls"];
|
|
823
986
|
if (!hasContent && !hasToolCalls) {
|
|
824
|
-
throw
|
|
825
|
-
"RILL-R004",
|
|
826
|
-
"assistant message requires 'content' or 'tool_calls'"
|
|
827
|
-
);
|
|
987
|
+
throw haltInvalid(ctx, "INVALID_INPUT", "invalid_assistant_message", "assistant message requires 'content' or 'tool_calls'");
|
|
828
988
|
}
|
|
829
989
|
if (hasContent) {
|
|
830
990
|
apiMessages.push({
|
|
@@ -850,7 +1010,7 @@ function createOpenAIExtension(config) {
|
|
|
850
1010
|
}
|
|
851
1011
|
}
|
|
852
1012
|
} catch (error) {
|
|
853
|
-
|
|
1013
|
+
throwProviderHalt(ctx, "OpenAI", error, detectOpenAIError);
|
|
854
1014
|
}
|
|
855
1015
|
}
|
|
856
1016
|
const resolve = async () => {
|
|
@@ -888,14 +1048,23 @@ function createOpenAIExtension(config) {
|
|
|
888
1048
|
return result;
|
|
889
1049
|
} catch (error) {
|
|
890
1050
|
const duration = Date.now() - startTime;
|
|
891
|
-
|
|
1051
|
+
if (error instanceof RuntimeHaltSignal3) {
|
|
1052
|
+
emitExtensionEvent(ctx, {
|
|
1053
|
+
event: "openai:error",
|
|
1054
|
+
subsystem: "extension:openai",
|
|
1055
|
+
error: getStatus2(error.value).message,
|
|
1056
|
+
duration
|
|
1057
|
+
});
|
|
1058
|
+
throw error;
|
|
1059
|
+
}
|
|
1060
|
+
const invalid = mapProviderError(ctx, "OpenAI", error, detectOpenAIError);
|
|
892
1061
|
emitExtensionEvent(ctx, {
|
|
893
1062
|
event: "openai:error",
|
|
894
1063
|
subsystem: "extension:openai",
|
|
895
|
-
error:
|
|
1064
|
+
error: getStatus2(invalid).message,
|
|
896
1065
|
duration
|
|
897
1066
|
});
|
|
898
|
-
throw
|
|
1067
|
+
throw new RuntimeHaltSignal3(invalid, true);
|
|
899
1068
|
}
|
|
900
1069
|
};
|
|
901
1070
|
const retType = {
|
|
@@ -952,10 +1121,7 @@ function createOpenAIExtension(config) {
|
|
|
952
1121
|
});
|
|
953
1122
|
const embeddingData = response.data[0]?.embedding;
|
|
954
1123
|
if (!embeddingData || embeddingData.length === 0) {
|
|
955
|
-
throw
|
|
956
|
-
"RILL-R004",
|
|
957
|
-
"OpenAI: empty embedding returned"
|
|
958
|
-
);
|
|
1124
|
+
throw haltInvalid(ctx, "PROTOCOL", "empty_embedding_response", "OpenAI: empty embedding returned");
|
|
959
1125
|
}
|
|
960
1126
|
const float32Data = new Float32Array(embeddingData);
|
|
961
1127
|
const vector = createVector(float32Data, factoryEmbedModel);
|
|
@@ -970,7 +1136,17 @@ function createOpenAIExtension(config) {
|
|
|
970
1136
|
return vector;
|
|
971
1137
|
} catch (error) {
|
|
972
1138
|
const duration = Date.now() - startTime;
|
|
973
|
-
|
|
1139
|
+
if (error instanceof RuntimeHaltSignal3) {
|
|
1140
|
+
emitExtensionEvent(ctx, {
|
|
1141
|
+
event: "openai:error",
|
|
1142
|
+
subsystem: "extension:openai",
|
|
1143
|
+
error: getStatus2(error.value).message,
|
|
1144
|
+
duration
|
|
1145
|
+
});
|
|
1146
|
+
throw error;
|
|
1147
|
+
}
|
|
1148
|
+
const invalid = mapProviderError(
|
|
1149
|
+
ctx,
|
|
974
1150
|
"OpenAI",
|
|
975
1151
|
error,
|
|
976
1152
|
detectOpenAIError
|
|
@@ -978,10 +1154,10 @@ function createOpenAIExtension(config) {
|
|
|
978
1154
|
emitExtensionEvent(ctx, {
|
|
979
1155
|
event: "openai:error",
|
|
980
1156
|
subsystem: "extension:openai",
|
|
981
|
-
error:
|
|
1157
|
+
error: getStatus2(invalid).message,
|
|
982
1158
|
duration
|
|
983
1159
|
});
|
|
984
|
-
throw
|
|
1160
|
+
throw new RuntimeHaltSignal3(invalid, true);
|
|
985
1161
|
}
|
|
986
1162
|
},
|
|
987
1163
|
annotations: { description: "Generate embedding vector for text" },
|
|
@@ -1008,10 +1184,7 @@ function createOpenAIExtension(config) {
|
|
|
1008
1184
|
for (const embeddingItem of response.data) {
|
|
1009
1185
|
const embeddingData = embeddingItem.embedding;
|
|
1010
1186
|
if (!embeddingData || embeddingData.length === 0) {
|
|
1011
|
-
throw
|
|
1012
|
-
"RILL-R004",
|
|
1013
|
-
"OpenAI: empty embedding returned"
|
|
1014
|
-
);
|
|
1187
|
+
throw haltInvalid(ctx, "PROTOCOL", "empty_embedding_response", "OpenAI: empty embedding returned");
|
|
1015
1188
|
}
|
|
1016
1189
|
const float32Data = new Float32Array(embeddingData);
|
|
1017
1190
|
const vector = createVector(float32Data, factoryEmbedModel);
|
|
@@ -1031,7 +1204,17 @@ function createOpenAIExtension(config) {
|
|
|
1031
1204
|
return vectors;
|
|
1032
1205
|
} catch (error) {
|
|
1033
1206
|
const duration = Date.now() - startTime;
|
|
1034
|
-
|
|
1207
|
+
if (error instanceof RuntimeHaltSignal3) {
|
|
1208
|
+
emitExtensionEvent(ctx, {
|
|
1209
|
+
event: "openai:error",
|
|
1210
|
+
subsystem: "extension:openai",
|
|
1211
|
+
error: getStatus2(error.value).message,
|
|
1212
|
+
duration
|
|
1213
|
+
});
|
|
1214
|
+
throw error;
|
|
1215
|
+
}
|
|
1216
|
+
const invalid = mapProviderError(
|
|
1217
|
+
ctx,
|
|
1035
1218
|
"OpenAI",
|
|
1036
1219
|
error,
|
|
1037
1220
|
detectOpenAIError
|
|
@@ -1039,10 +1222,10 @@ function createOpenAIExtension(config) {
|
|
|
1039
1222
|
emitExtensionEvent(ctx, {
|
|
1040
1223
|
event: "openai:error",
|
|
1041
1224
|
subsystem: "extension:openai",
|
|
1042
|
-
error:
|
|
1225
|
+
error: getStatus2(invalid).message,
|
|
1043
1226
|
duration
|
|
1044
1227
|
});
|
|
1045
|
-
throw
|
|
1228
|
+
throw new RuntimeHaltSignal3(invalid, true);
|
|
1046
1229
|
}
|
|
1047
1230
|
},
|
|
1048
1231
|
annotations: { description: "Generate embedding vectors for multiple texts" },
|
|
@@ -1071,7 +1254,7 @@ function createOpenAIExtension(config) {
|
|
|
1071
1254
|
const toolsDict = args["tools"];
|
|
1072
1255
|
const options = args["options"] ?? {};
|
|
1073
1256
|
if (prompt.trim().length === 0) {
|
|
1074
|
-
throw
|
|
1257
|
+
throw haltInvalid(ctx, "INVALID_INPUT", "empty_prompt", "prompt text cannot be empty");
|
|
1075
1258
|
}
|
|
1076
1259
|
const system = typeof options["system"] === "string" ? options["system"] : factorySystem;
|
|
1077
1260
|
const maxTokens = typeof options["max_tokens"] === "number" && options["max_tokens"] > 0 ? options["max_tokens"] : factoryMaxTokens;
|
|
@@ -1088,20 +1271,14 @@ function createOpenAIExtension(config) {
|
|
|
1088
1271
|
const prependedMessages = options["messages"];
|
|
1089
1272
|
for (const msg of prependedMessages) {
|
|
1090
1273
|
if (!msg || typeof msg !== "object" || !("role" in msg)) {
|
|
1091
|
-
throw
|
|
1092
|
-
"RILL-R004",
|
|
1093
|
-
"message missing required 'role' field"
|
|
1094
|
-
);
|
|
1274
|
+
throw haltInvalid(ctx, "INVALID_INPUT", "invalid_message_format", "message missing required 'role' field");
|
|
1095
1275
|
}
|
|
1096
1276
|
const role = msg["role"];
|
|
1097
1277
|
if (role !== "user" && role !== "assistant") {
|
|
1098
|
-
throw
|
|
1278
|
+
throw haltInvalid(ctx, "INVALID_INPUT", "invalid_role", `invalid role '${String(role)}'`);
|
|
1099
1279
|
}
|
|
1100
1280
|
if (!("content" in msg) || typeof msg["content"] !== "string") {
|
|
1101
|
-
throw
|
|
1102
|
-
"RILL-R004",
|
|
1103
|
-
`${role} message requires 'content'`
|
|
1104
|
-
);
|
|
1281
|
+
throw haltInvalid(ctx, "INVALID_INPUT", "missing_message_content", `${role} message requires 'content'`);
|
|
1105
1282
|
}
|
|
1106
1283
|
messages.push({
|
|
1107
1284
|
role,
|
|
@@ -1278,8 +1455,8 @@ function createOpenAIExtension(config) {
|
|
|
1278
1455
|
yield chunk;
|
|
1279
1456
|
}
|
|
1280
1457
|
} catch (error) {
|
|
1281
|
-
|
|
1282
|
-
|
|
1458
|
+
if (error instanceof RuntimeHaltSignal3) throw error;
|
|
1459
|
+
throwProviderHalt(ctx, "OpenAI", error, detectOpenAIError);
|
|
1283
1460
|
}
|
|
1284
1461
|
}
|
|
1285
1462
|
const resolve = async () => {
|
|
@@ -1320,14 +1497,23 @@ function createOpenAIExtension(config) {
|
|
|
1320
1497
|
return result;
|
|
1321
1498
|
} catch (error) {
|
|
1322
1499
|
const duration = Date.now() - startTime;
|
|
1323
|
-
|
|
1500
|
+
if (error instanceof RuntimeHaltSignal3) {
|
|
1501
|
+
emitExtensionEvent(ctx, {
|
|
1502
|
+
event: "openai:error",
|
|
1503
|
+
subsystem: "extension:openai",
|
|
1504
|
+
error: getStatus2(error.value).message,
|
|
1505
|
+
duration
|
|
1506
|
+
});
|
|
1507
|
+
throw error;
|
|
1508
|
+
}
|
|
1509
|
+
const invalid = mapProviderError(ctx, "OpenAI", error, detectOpenAIError);
|
|
1324
1510
|
emitExtensionEvent(ctx, {
|
|
1325
1511
|
event: "openai:error",
|
|
1326
1512
|
subsystem: "extension:openai",
|
|
1327
|
-
error:
|
|
1513
|
+
error: getStatus2(invalid).message,
|
|
1328
1514
|
duration
|
|
1329
1515
|
});
|
|
1330
|
-
throw
|
|
1516
|
+
throw new RuntimeHaltSignal3(invalid, true);
|
|
1331
1517
|
}
|
|
1332
1518
|
};
|
|
1333
1519
|
const retType = {
|
|
@@ -1386,16 +1572,10 @@ function createOpenAIExtension(config) {
|
|
|
1386
1572
|
const schemaArg = args["schema"];
|
|
1387
1573
|
const options = args["options"] ?? {};
|
|
1388
1574
|
if (!schemaArg || !schemaArg.__rill_type || !schemaArg.structure) {
|
|
1389
|
-
throw
|
|
1390
|
-
"RILL-R004",
|
|
1391
|
-
"generate requires a type expression as schema"
|
|
1392
|
-
);
|
|
1575
|
+
throw haltInvalid(ctx, "INVALID_INPUT", "invalid_schema", "generate requires a type expression as schema");
|
|
1393
1576
|
}
|
|
1394
1577
|
if (schemaArg.structure.kind !== "dict") {
|
|
1395
|
-
throw
|
|
1396
|
-
"RILL-R004",
|
|
1397
|
-
`generate requires a dict type as schema, got ${schemaArg.structure.kind}`
|
|
1398
|
-
);
|
|
1578
|
+
throw haltInvalid(ctx, "INVALID_INPUT", "invalid_schema_type", `generate requires a dict type as schema, got ${schemaArg.structure.kind}`);
|
|
1399
1579
|
}
|
|
1400
1580
|
const jsonSchema = buildJsonSchemaFromStructuralType(schemaArg.structure);
|
|
1401
1581
|
const system = typeof options["system"] === "string" ? options["system"] : factorySystem;
|
|
@@ -1411,20 +1591,14 @@ function createOpenAIExtension(config) {
|
|
|
1411
1591
|
const prependedMessages = options["messages"];
|
|
1412
1592
|
for (const msg of prependedMessages) {
|
|
1413
1593
|
if (!msg || typeof msg !== "object" || !("role" in msg)) {
|
|
1414
|
-
throw
|
|
1415
|
-
"RILL-R004",
|
|
1416
|
-
"message missing required 'role' field"
|
|
1417
|
-
);
|
|
1594
|
+
throw haltInvalid(ctx, "INVALID_INPUT", "invalid_message_format", "message missing required 'role' field");
|
|
1418
1595
|
}
|
|
1419
1596
|
const role = msg["role"];
|
|
1420
1597
|
if (role !== "user" && role !== "assistant") {
|
|
1421
|
-
throw
|
|
1598
|
+
throw haltInvalid(ctx, "INVALID_INPUT", "invalid_role", `invalid role '${String(role)}'`);
|
|
1422
1599
|
}
|
|
1423
1600
|
if (!("content" in msg) || typeof msg["content"] !== "string") {
|
|
1424
|
-
throw
|
|
1425
|
-
"RILL-R004",
|
|
1426
|
-
`${role} message requires 'content'`
|
|
1427
|
-
);
|
|
1601
|
+
throw haltInvalid(ctx, "INVALID_INPUT", "missing_message_content", `${role} message requires 'content'`);
|
|
1428
1602
|
}
|
|
1429
1603
|
apiMessages.push({
|
|
1430
1604
|
role,
|
|
@@ -1456,10 +1630,7 @@ function createOpenAIExtension(config) {
|
|
|
1456
1630
|
data = JSON.parse(raw);
|
|
1457
1631
|
} catch (parseError) {
|
|
1458
1632
|
const detail = parseError instanceof Error ? parseError.message : String(parseError);
|
|
1459
|
-
throw
|
|
1460
|
-
"RILL-R004",
|
|
1461
|
-
`generate: failed to parse response JSON: ${detail}`
|
|
1462
|
-
);
|
|
1633
|
+
throw haltInvalid(ctx, "PROTOCOL", "json_parse_failed", `generate: failed to parse response JSON: ${detail}`);
|
|
1463
1634
|
}
|
|
1464
1635
|
const result = {
|
|
1465
1636
|
data,
|
|
@@ -1485,14 +1656,32 @@ function createOpenAIExtension(config) {
|
|
|
1485
1656
|
return result;
|
|
1486
1657
|
} catch (error) {
|
|
1487
1658
|
const duration = Date.now() - startTime;
|
|
1488
|
-
|
|
1659
|
+
if (error instanceof RuntimeError5) {
|
|
1660
|
+
emitExtensionEvent(ctx, {
|
|
1661
|
+
event: "openai:error",
|
|
1662
|
+
subsystem: "extension:openai",
|
|
1663
|
+
error: error.message,
|
|
1664
|
+
duration
|
|
1665
|
+
});
|
|
1666
|
+
throw error;
|
|
1667
|
+
}
|
|
1668
|
+
if (error instanceof RuntimeHaltSignal3) {
|
|
1669
|
+
emitExtensionEvent(ctx, {
|
|
1670
|
+
event: "openai:error",
|
|
1671
|
+
subsystem: "extension:openai",
|
|
1672
|
+
error: getStatus2(error.value).message,
|
|
1673
|
+
duration
|
|
1674
|
+
});
|
|
1675
|
+
throw error;
|
|
1676
|
+
}
|
|
1677
|
+
const invalid = mapProviderError(ctx, "OpenAI", error, detectOpenAIError);
|
|
1489
1678
|
emitExtensionEvent(ctx, {
|
|
1490
1679
|
event: "openai:error",
|
|
1491
1680
|
subsystem: "extension:openai",
|
|
1492
|
-
error:
|
|
1681
|
+
error: getStatus2(invalid).message,
|
|
1493
1682
|
duration
|
|
1494
1683
|
});
|
|
1495
|
-
throw
|
|
1684
|
+
throw new RuntimeHaltSignal3(invalid, true);
|
|
1496
1685
|
}
|
|
1497
1686
|
},
|
|
1498
1687
|
annotations: { description: "Generate structured output from OpenAI API" },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rcrsr/rill-ext-openai",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.1",
|
|
4
4
|
"description": "rill extension for OpenAI API integration",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Andre Bremer",
|
|
@@ -17,16 +17,16 @@
|
|
|
17
17
|
"scripting"
|
|
18
18
|
],
|
|
19
19
|
"peerDependencies": {
|
|
20
|
-
"@rcrsr/rill": "~0.
|
|
20
|
+
"@rcrsr/rill": "~0.19.0"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
|
-
"@rcrsr/rill": "~0.
|
|
23
|
+
"@rcrsr/rill": "~0.19.0",
|
|
24
24
|
"@types/node": "^25.5.2",
|
|
25
25
|
"dts-bundle-generator": "^9.5.1",
|
|
26
26
|
"tsup": "^8.5.1",
|
|
27
27
|
"undici-types": "^8.0.2",
|
|
28
|
-
"@rcrsr/rill-ext-llm-shared": "^0.
|
|
29
|
-
"@rcrsr/rill-ext-param-shared": "0.
|
|
28
|
+
"@rcrsr/rill-ext-llm-shared": "^0.19.0",
|
|
29
|
+
"@rcrsr/rill-ext-param-shared": "0.19.0"
|
|
30
30
|
},
|
|
31
31
|
"files": [
|
|
32
32
|
"dist"
|