@rcrsr/rill-ext-openai 0.18.4 → 0.19.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/index.js +295 -111
- 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,13 +209,13 @@ 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 {};
|
|
@@ -153,7 +265,7 @@ function buildJsonSchemaFromStructuralType(type, params) {
|
|
|
153
265
|
return buildDictSchema(type);
|
|
154
266
|
}
|
|
155
267
|
if (type.kind !== "closure") {
|
|
156
|
-
throw new
|
|
268
|
+
throw new RuntimeError2("RILL-R005", `unsupported schema kind: ${type.kind} (expected dict or closure)`);
|
|
157
269
|
}
|
|
158
270
|
const properties = {};
|
|
159
271
|
const required = [];
|
|
@@ -181,30 +293,51 @@ function buildJsonSchemaFromStructuralType(type, params) {
|
|
|
181
293
|
}
|
|
182
294
|
|
|
183
295
|
// ../../shared/ext-llm/dist/tool-loop.js
|
|
296
|
+
function readHaltMessage(halt) {
|
|
297
|
+
const status = getStatus(halt.value);
|
|
298
|
+
if (typeof status.message === "string" && status.message.length > 0) {
|
|
299
|
+
return status.message;
|
|
300
|
+
}
|
|
301
|
+
return halt.message;
|
|
302
|
+
}
|
|
303
|
+
function throwToolLoopHalt(ctx, code, kind, message) {
|
|
304
|
+
if (ctx !== void 0) {
|
|
305
|
+
const hostCtx = ctx;
|
|
306
|
+
if (typeof hostCtx.invalidate === "function") {
|
|
307
|
+
const invalid = hostCtx.invalidate(new Error(message), {
|
|
308
|
+
code,
|
|
309
|
+
provider: "tool_loop",
|
|
310
|
+
raw: { kind, message }
|
|
311
|
+
});
|
|
312
|
+
throw new RuntimeHaltSignal2(invalid, true);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
throw new RuntimeError3("RILL-R005", message);
|
|
316
|
+
}
|
|
184
317
|
async function executeToolCall(toolName, toolInput, tools, context) {
|
|
185
318
|
if (!isDict(tools)) {
|
|
186
|
-
|
|
319
|
+
throwToolLoopHalt(context, "INVALID_INPUT", "tools_not_dict", "tool_loop: tools must be a dict of name \u2192 callable");
|
|
187
320
|
}
|
|
188
321
|
const toolsDict = tools;
|
|
189
322
|
const toolFn = toolsDict[toolName];
|
|
190
323
|
if (toolFn === void 0 || toolFn === null) {
|
|
191
|
-
|
|
324
|
+
throwToolLoopHalt(context, "NOT_FOUND", "unknown_tool", `Unknown tool: ${toolName}`);
|
|
192
325
|
}
|
|
193
326
|
if (!isCallable(toolFn)) {
|
|
194
|
-
|
|
327
|
+
throwToolLoopHalt(context, "INVALID_INPUT", "tool_not_callable", `Invalid tool input for ${toolName}: tool must be callable`);
|
|
195
328
|
}
|
|
196
329
|
if (typeof toolInput !== "object" || toolInput === null) {
|
|
197
|
-
|
|
330
|
+
throwToolLoopHalt(context, "INVALID_INPUT", "invalid_tool_input", `Invalid tool input for ${toolName}: input must be an object`);
|
|
198
331
|
}
|
|
199
332
|
const callable = toolFn;
|
|
200
333
|
if (callable.kind !== "runtime" && callable.kind !== "application" && callable.kind !== "script") {
|
|
201
|
-
|
|
334
|
+
throwToolLoopHalt(context, "INVALID_INPUT", "tool_kind_unsupported", `Invalid tool input for ${toolName}: tool must be application, runtime, or script callable`);
|
|
202
335
|
}
|
|
203
336
|
try {
|
|
204
337
|
const inputDict = toolInput;
|
|
205
338
|
if (callable.kind === "script") {
|
|
206
339
|
if (!context) {
|
|
207
|
-
throw new
|
|
340
|
+
throw new RuntimeError3("RILL-R005", `Invalid tool input for ${toolName}: script callable requires a runtime context`);
|
|
208
341
|
}
|
|
209
342
|
let args;
|
|
210
343
|
if (callable.params && callable.params.length > 0) {
|
|
@@ -220,16 +353,17 @@ async function executeToolCall(toolName, toolInput, tools, context) {
|
|
|
220
353
|
const ctx = context ?? {
|
|
221
354
|
parent: void 0,
|
|
222
355
|
variables: /* @__PURE__ */ new Map(),
|
|
223
|
-
pipeValue: null
|
|
356
|
+
pipeValue: null,
|
|
357
|
+
hostContext: {}
|
|
224
358
|
};
|
|
225
359
|
const result = callable.fn(inputDict, ctx);
|
|
226
360
|
return result instanceof Promise ? await result : result;
|
|
227
361
|
} catch (error) {
|
|
228
|
-
if (error instanceof
|
|
362
|
+
if (error instanceof RuntimeError3) {
|
|
229
363
|
throw error;
|
|
230
364
|
}
|
|
231
365
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
232
|
-
throw new
|
|
366
|
+
throw new RuntimeError3("RILL-R005", `Invalid tool input for ${toolName}: ${message}`);
|
|
233
367
|
}
|
|
234
368
|
}
|
|
235
369
|
function sanitizeToolName(name) {
|
|
@@ -300,19 +434,19 @@ function patchResponseToolCallNames(response, nameMap) {
|
|
|
300
434
|
}
|
|
301
435
|
async function executeToolLoop(messages, tools, maxErrors, callbacks, emitEvent, maxTurns = 10, context, yieldChunk, signal) {
|
|
302
436
|
if (tools === void 0) {
|
|
303
|
-
|
|
437
|
+
throwToolLoopHalt(context, "INVALID_INPUT", "tools_required", "tools parameter is required");
|
|
304
438
|
}
|
|
305
439
|
if (!isDict(tools)) {
|
|
306
|
-
|
|
440
|
+
throwToolLoopHalt(context, "INVALID_INPUT", "tools_not_dict", "tool_loop: tools must be a dict of name \u2192 callable");
|
|
307
441
|
}
|
|
308
442
|
const toolsDict = tools;
|
|
309
443
|
const toolDescriptors = Object.entries(toolsDict).map(([name, fn]) => {
|
|
310
444
|
const fnValue = fn;
|
|
311
445
|
if (isRuntimeCallable(fnValue)) {
|
|
312
|
-
|
|
446
|
+
throwToolLoopHalt(context, "INVALID_INPUT", "builtin_tool_unsupported", `tool_loop: builtin "${name}" cannot be used as a tool \u2014 wrap in a closure`);
|
|
313
447
|
}
|
|
314
448
|
if (!isCallable(fnValue)) {
|
|
315
|
-
|
|
449
|
+
throwToolLoopHalt(context, "INVALID_INPUT", "tool_not_callable", `tool_loop: tool "${name}" is not a callable`);
|
|
316
450
|
}
|
|
317
451
|
const callable = fnValue;
|
|
318
452
|
const description = callable.annotations?.["description"] ?? "";
|
|
@@ -347,7 +481,7 @@ async function executeToolLoop(messages, tools, maxErrors, callbacks, emitEvent,
|
|
|
347
481
|
let turnCount = 0;
|
|
348
482
|
while (turnCount < maxTurns) {
|
|
349
483
|
if (signal?.aborted) {
|
|
350
|
-
|
|
484
|
+
throwToolLoopHalt(context, "TIMEOUT", "tool_loop_cancelled", "tool_loop cancelled");
|
|
351
485
|
}
|
|
352
486
|
turnCount++;
|
|
353
487
|
let response;
|
|
@@ -361,8 +495,18 @@ async function executeToolLoop(messages, tools, maxErrors, callbacks, emitEvent,
|
|
|
361
495
|
response = await callbacks.callAPI(currentMessages, providerTools, signal);
|
|
362
496
|
}
|
|
363
497
|
} catch (error) {
|
|
498
|
+
if (error instanceof RuntimeHaltSignal2) {
|
|
499
|
+
throw error;
|
|
500
|
+
}
|
|
501
|
+
if (context !== void 0 && callbacks.detectError !== void 0) {
|
|
502
|
+
const hostCtx = context;
|
|
503
|
+
if (typeof hostCtx.invalidate === "function") {
|
|
504
|
+
const invalid = mapProviderError(hostCtx, "tool_loop", error, callbacks.detectError);
|
|
505
|
+
throw new RuntimeHaltSignal2(invalid, true);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
364
508
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
365
|
-
throw new
|
|
509
|
+
throw new RuntimeError3("RILL-R005", `Provider API error: ${message}`, void 0, { cause: error });
|
|
366
510
|
}
|
|
367
511
|
if (typeof response === "object" && response !== null && "usage" in response) {
|
|
368
512
|
const usage = response["usage"];
|
|
@@ -419,7 +563,11 @@ async function executeToolLoop(messages, tools, maxErrors, callbacks, emitEvent,
|
|
|
419
563
|
const duration = Date.now() - toolStartTime;
|
|
420
564
|
consecutiveErrors++;
|
|
421
565
|
let originalError;
|
|
422
|
-
if (error instanceof
|
|
566
|
+
if (error instanceof RuntimeHaltSignal2) {
|
|
567
|
+
const haltMessage = readHaltMessage(error);
|
|
568
|
+
const prefix = `Invalid tool input for ${name}: `;
|
|
569
|
+
originalError = haltMessage.startsWith(prefix) ? haltMessage.slice(prefix.length) : haltMessage;
|
|
570
|
+
} else if (error instanceof RuntimeError3) {
|
|
423
571
|
const prefix = `Invalid tool input for ${name}: `;
|
|
424
572
|
if (error.message.startsWith(prefix)) {
|
|
425
573
|
originalError = error.message.slice(prefix.length);
|
|
@@ -444,7 +592,7 @@ async function executeToolLoop(messages, tools, maxErrors, callbacks, emitEvent,
|
|
|
444
592
|
duration
|
|
445
593
|
});
|
|
446
594
|
if (consecutiveErrors >= maxErrors) {
|
|
447
|
-
|
|
595
|
+
throwToolLoopHalt(context, "UNAVAILABLE", "consecutive_tool_errors", `Tool execution failed: ${maxErrors} consecutive errors (last: ${name}: ${originalError})`);
|
|
448
596
|
}
|
|
449
597
|
}
|
|
450
598
|
}
|
|
@@ -471,13 +619,13 @@ function buildResponseMessages(inputMessages, assistantContent) {
|
|
|
471
619
|
}
|
|
472
620
|
|
|
473
621
|
// ../../shared/ext-param/dist/param.js
|
|
474
|
-
import { RuntimeError as
|
|
622
|
+
import { RuntimeError as RuntimeError4 } from "@rcrsr/rill";
|
|
475
623
|
function validateParamName(name) {
|
|
476
624
|
if (name === "") {
|
|
477
|
-
throw new
|
|
625
|
+
throw new RuntimeError4("RILL-R001", "param name must not be empty");
|
|
478
626
|
}
|
|
479
627
|
if (/\s/.test(name)) {
|
|
480
|
-
throw new
|
|
628
|
+
throw new RuntimeError4("RILL-R001", "param name must be a valid identifier");
|
|
481
629
|
}
|
|
482
630
|
}
|
|
483
631
|
function buildAnnotations(desc) {
|
|
@@ -603,6 +751,16 @@ var detectOpenAIError = (error) => {
|
|
|
603
751
|
}
|
|
604
752
|
return null;
|
|
605
753
|
};
|
|
754
|
+
function haltInvalid(ctx, code, rawKind, message) {
|
|
755
|
+
return new RuntimeHaltSignal3(
|
|
756
|
+
ctx.invalidate(new Error(message), {
|
|
757
|
+
code,
|
|
758
|
+
provider: "openai",
|
|
759
|
+
raw: { kind: rawKind, message }
|
|
760
|
+
}),
|
|
761
|
+
true
|
|
762
|
+
);
|
|
763
|
+
}
|
|
606
764
|
function createOpenAIExtension(config) {
|
|
607
765
|
validateApiKey(config.api_key);
|
|
608
766
|
validateModel(config.model);
|
|
@@ -650,7 +808,7 @@ function createOpenAIExtension(config) {
|
|
|
650
808
|
const text = args["text"];
|
|
651
809
|
const options = args["options"] ?? {};
|
|
652
810
|
if (text.trim().length === 0) {
|
|
653
|
-
throw
|
|
811
|
+
throw haltInvalid(ctx, "INVALID_INPUT", "empty_prompt", "prompt text cannot be empty");
|
|
654
812
|
}
|
|
655
813
|
const system = typeof options["system"] === "string" ? options["system"] : factorySystem;
|
|
656
814
|
const maxTokens = typeof options["max_tokens"] === "number" && options["max_tokens"] > 0 ? options["max_tokens"] : factoryMaxTokens;
|
|
@@ -681,7 +839,7 @@ function createOpenAIExtension(config) {
|
|
|
681
839
|
}
|
|
682
840
|
}
|
|
683
841
|
} catch (error) {
|
|
684
|
-
|
|
842
|
+
throwProviderHalt(ctx, "OpenAI", error, detectOpenAIError);
|
|
685
843
|
}
|
|
686
844
|
}
|
|
687
845
|
const resolve = async () => {
|
|
@@ -719,14 +877,23 @@ function createOpenAIExtension(config) {
|
|
|
719
877
|
return result;
|
|
720
878
|
} catch (error) {
|
|
721
879
|
const duration = Date.now() - startTime;
|
|
722
|
-
|
|
880
|
+
if (error instanceof RuntimeHaltSignal3) {
|
|
881
|
+
emitExtensionEvent(ctx, {
|
|
882
|
+
event: "openai:error",
|
|
883
|
+
subsystem: "extension:openai",
|
|
884
|
+
error: getStatus2(error.value).message,
|
|
885
|
+
duration
|
|
886
|
+
});
|
|
887
|
+
throw error;
|
|
888
|
+
}
|
|
889
|
+
const invalid = mapProviderError(ctx, "OpenAI", error, detectOpenAIError);
|
|
723
890
|
emitExtensionEvent(ctx, {
|
|
724
891
|
event: "openai:error",
|
|
725
892
|
subsystem: "extension:openai",
|
|
726
|
-
error:
|
|
893
|
+
error: getStatus2(invalid).message,
|
|
727
894
|
duration
|
|
728
895
|
});
|
|
729
|
-
throw
|
|
896
|
+
throw new RuntimeHaltSignal3(invalid, true);
|
|
730
897
|
}
|
|
731
898
|
};
|
|
732
899
|
const retType = {
|
|
@@ -780,10 +947,7 @@ function createOpenAIExtension(config) {
|
|
|
780
947
|
const messages = args["messages"];
|
|
781
948
|
const options = args["options"] ?? {};
|
|
782
949
|
if (messages.length === 0) {
|
|
783
|
-
throw
|
|
784
|
-
"RILL-R004",
|
|
785
|
-
"messages list cannot be empty"
|
|
786
|
-
);
|
|
950
|
+
throw haltInvalid(ctx, "INVALID_INPUT", "empty_messages", "messages list cannot be empty");
|
|
787
951
|
}
|
|
788
952
|
const system = typeof options["system"] === "string" ? options["system"] : factorySystem;
|
|
789
953
|
const maxTokens = typeof options["max_tokens"] === "number" && options["max_tokens"] > 0 ? options["max_tokens"] : factoryMaxTokens;
|
|
@@ -797,21 +961,15 @@ function createOpenAIExtension(config) {
|
|
|
797
961
|
for (let i = 0; i < messages.length; i++) {
|
|
798
962
|
const msg = messages[i];
|
|
799
963
|
if (!msg || typeof msg !== "object" || !("role" in msg)) {
|
|
800
|
-
throw
|
|
801
|
-
"RILL-R004",
|
|
802
|
-
"message missing required 'role' field"
|
|
803
|
-
);
|
|
964
|
+
throw haltInvalid(ctx, "INVALID_INPUT", "invalid_message_format", "message missing required 'role' field");
|
|
804
965
|
}
|
|
805
966
|
const role = msg["role"];
|
|
806
967
|
if (role !== "user" && role !== "assistant" && role !== "tool") {
|
|
807
|
-
throw
|
|
968
|
+
throw haltInvalid(ctx, "INVALID_INPUT", "invalid_role", `invalid role '${String(role)}'`);
|
|
808
969
|
}
|
|
809
970
|
if (role === "user" || role === "tool") {
|
|
810
971
|
if (!("content" in msg) || typeof msg["content"] !== "string") {
|
|
811
|
-
throw
|
|
812
|
-
"RILL-R004",
|
|
813
|
-
`${role} message requires 'content'`
|
|
814
|
-
);
|
|
972
|
+
throw haltInvalid(ctx, "INVALID_INPUT", "missing_message_content", `${role} message requires 'content'`);
|
|
815
973
|
}
|
|
816
974
|
apiMessages.push({
|
|
817
975
|
role,
|
|
@@ -821,10 +979,7 @@ function createOpenAIExtension(config) {
|
|
|
821
979
|
const hasContent = "content" in msg && msg["content"];
|
|
822
980
|
const hasToolCalls = "tool_calls" in msg && msg["tool_calls"];
|
|
823
981
|
if (!hasContent && !hasToolCalls) {
|
|
824
|
-
throw
|
|
825
|
-
"RILL-R004",
|
|
826
|
-
"assistant message requires 'content' or 'tool_calls'"
|
|
827
|
-
);
|
|
982
|
+
throw haltInvalid(ctx, "INVALID_INPUT", "invalid_assistant_message", "assistant message requires 'content' or 'tool_calls'");
|
|
828
983
|
}
|
|
829
984
|
if (hasContent) {
|
|
830
985
|
apiMessages.push({
|
|
@@ -850,7 +1005,7 @@ function createOpenAIExtension(config) {
|
|
|
850
1005
|
}
|
|
851
1006
|
}
|
|
852
1007
|
} catch (error) {
|
|
853
|
-
|
|
1008
|
+
throwProviderHalt(ctx, "OpenAI", error, detectOpenAIError);
|
|
854
1009
|
}
|
|
855
1010
|
}
|
|
856
1011
|
const resolve = async () => {
|
|
@@ -888,14 +1043,23 @@ function createOpenAIExtension(config) {
|
|
|
888
1043
|
return result;
|
|
889
1044
|
} catch (error) {
|
|
890
1045
|
const duration = Date.now() - startTime;
|
|
891
|
-
|
|
1046
|
+
if (error instanceof RuntimeHaltSignal3) {
|
|
1047
|
+
emitExtensionEvent(ctx, {
|
|
1048
|
+
event: "openai:error",
|
|
1049
|
+
subsystem: "extension:openai",
|
|
1050
|
+
error: getStatus2(error.value).message,
|
|
1051
|
+
duration
|
|
1052
|
+
});
|
|
1053
|
+
throw error;
|
|
1054
|
+
}
|
|
1055
|
+
const invalid = mapProviderError(ctx, "OpenAI", error, detectOpenAIError);
|
|
892
1056
|
emitExtensionEvent(ctx, {
|
|
893
1057
|
event: "openai:error",
|
|
894
1058
|
subsystem: "extension:openai",
|
|
895
|
-
error:
|
|
1059
|
+
error: getStatus2(invalid).message,
|
|
896
1060
|
duration
|
|
897
1061
|
});
|
|
898
|
-
throw
|
|
1062
|
+
throw new RuntimeHaltSignal3(invalid, true);
|
|
899
1063
|
}
|
|
900
1064
|
};
|
|
901
1065
|
const retType = {
|
|
@@ -952,10 +1116,7 @@ function createOpenAIExtension(config) {
|
|
|
952
1116
|
});
|
|
953
1117
|
const embeddingData = response.data[0]?.embedding;
|
|
954
1118
|
if (!embeddingData || embeddingData.length === 0) {
|
|
955
|
-
throw
|
|
956
|
-
"RILL-R004",
|
|
957
|
-
"OpenAI: empty embedding returned"
|
|
958
|
-
);
|
|
1119
|
+
throw haltInvalid(ctx, "PROTOCOL", "empty_embedding_response", "OpenAI: empty embedding returned");
|
|
959
1120
|
}
|
|
960
1121
|
const float32Data = new Float32Array(embeddingData);
|
|
961
1122
|
const vector = createVector(float32Data, factoryEmbedModel);
|
|
@@ -970,7 +1131,17 @@ function createOpenAIExtension(config) {
|
|
|
970
1131
|
return vector;
|
|
971
1132
|
} catch (error) {
|
|
972
1133
|
const duration = Date.now() - startTime;
|
|
973
|
-
|
|
1134
|
+
if (error instanceof RuntimeHaltSignal3) {
|
|
1135
|
+
emitExtensionEvent(ctx, {
|
|
1136
|
+
event: "openai:error",
|
|
1137
|
+
subsystem: "extension:openai",
|
|
1138
|
+
error: getStatus2(error.value).message,
|
|
1139
|
+
duration
|
|
1140
|
+
});
|
|
1141
|
+
throw error;
|
|
1142
|
+
}
|
|
1143
|
+
const invalid = mapProviderError(
|
|
1144
|
+
ctx,
|
|
974
1145
|
"OpenAI",
|
|
975
1146
|
error,
|
|
976
1147
|
detectOpenAIError
|
|
@@ -978,10 +1149,10 @@ function createOpenAIExtension(config) {
|
|
|
978
1149
|
emitExtensionEvent(ctx, {
|
|
979
1150
|
event: "openai:error",
|
|
980
1151
|
subsystem: "extension:openai",
|
|
981
|
-
error:
|
|
1152
|
+
error: getStatus2(invalid).message,
|
|
982
1153
|
duration
|
|
983
1154
|
});
|
|
984
|
-
throw
|
|
1155
|
+
throw new RuntimeHaltSignal3(invalid, true);
|
|
985
1156
|
}
|
|
986
1157
|
},
|
|
987
1158
|
annotations: { description: "Generate embedding vector for text" },
|
|
@@ -1008,10 +1179,7 @@ function createOpenAIExtension(config) {
|
|
|
1008
1179
|
for (const embeddingItem of response.data) {
|
|
1009
1180
|
const embeddingData = embeddingItem.embedding;
|
|
1010
1181
|
if (!embeddingData || embeddingData.length === 0) {
|
|
1011
|
-
throw
|
|
1012
|
-
"RILL-R004",
|
|
1013
|
-
"OpenAI: empty embedding returned"
|
|
1014
|
-
);
|
|
1182
|
+
throw haltInvalid(ctx, "PROTOCOL", "empty_embedding_response", "OpenAI: empty embedding returned");
|
|
1015
1183
|
}
|
|
1016
1184
|
const float32Data = new Float32Array(embeddingData);
|
|
1017
1185
|
const vector = createVector(float32Data, factoryEmbedModel);
|
|
@@ -1031,7 +1199,17 @@ function createOpenAIExtension(config) {
|
|
|
1031
1199
|
return vectors;
|
|
1032
1200
|
} catch (error) {
|
|
1033
1201
|
const duration = Date.now() - startTime;
|
|
1034
|
-
|
|
1202
|
+
if (error instanceof RuntimeHaltSignal3) {
|
|
1203
|
+
emitExtensionEvent(ctx, {
|
|
1204
|
+
event: "openai:error",
|
|
1205
|
+
subsystem: "extension:openai",
|
|
1206
|
+
error: getStatus2(error.value).message,
|
|
1207
|
+
duration
|
|
1208
|
+
});
|
|
1209
|
+
throw error;
|
|
1210
|
+
}
|
|
1211
|
+
const invalid = mapProviderError(
|
|
1212
|
+
ctx,
|
|
1035
1213
|
"OpenAI",
|
|
1036
1214
|
error,
|
|
1037
1215
|
detectOpenAIError
|
|
@@ -1039,10 +1217,10 @@ function createOpenAIExtension(config) {
|
|
|
1039
1217
|
emitExtensionEvent(ctx, {
|
|
1040
1218
|
event: "openai:error",
|
|
1041
1219
|
subsystem: "extension:openai",
|
|
1042
|
-
error:
|
|
1220
|
+
error: getStatus2(invalid).message,
|
|
1043
1221
|
duration
|
|
1044
1222
|
});
|
|
1045
|
-
throw
|
|
1223
|
+
throw new RuntimeHaltSignal3(invalid, true);
|
|
1046
1224
|
}
|
|
1047
1225
|
},
|
|
1048
1226
|
annotations: { description: "Generate embedding vectors for multiple texts" },
|
|
@@ -1071,7 +1249,7 @@ function createOpenAIExtension(config) {
|
|
|
1071
1249
|
const toolsDict = args["tools"];
|
|
1072
1250
|
const options = args["options"] ?? {};
|
|
1073
1251
|
if (prompt.trim().length === 0) {
|
|
1074
|
-
throw
|
|
1252
|
+
throw haltInvalid(ctx, "INVALID_INPUT", "empty_prompt", "prompt text cannot be empty");
|
|
1075
1253
|
}
|
|
1076
1254
|
const system = typeof options["system"] === "string" ? options["system"] : factorySystem;
|
|
1077
1255
|
const maxTokens = typeof options["max_tokens"] === "number" && options["max_tokens"] > 0 ? options["max_tokens"] : factoryMaxTokens;
|
|
@@ -1088,20 +1266,14 @@ function createOpenAIExtension(config) {
|
|
|
1088
1266
|
const prependedMessages = options["messages"];
|
|
1089
1267
|
for (const msg of prependedMessages) {
|
|
1090
1268
|
if (!msg || typeof msg !== "object" || !("role" in msg)) {
|
|
1091
|
-
throw
|
|
1092
|
-
"RILL-R004",
|
|
1093
|
-
"message missing required 'role' field"
|
|
1094
|
-
);
|
|
1269
|
+
throw haltInvalid(ctx, "INVALID_INPUT", "invalid_message_format", "message missing required 'role' field");
|
|
1095
1270
|
}
|
|
1096
1271
|
const role = msg["role"];
|
|
1097
1272
|
if (role !== "user" && role !== "assistant") {
|
|
1098
|
-
throw
|
|
1273
|
+
throw haltInvalid(ctx, "INVALID_INPUT", "invalid_role", `invalid role '${String(role)}'`);
|
|
1099
1274
|
}
|
|
1100
1275
|
if (!("content" in msg) || typeof msg["content"] !== "string") {
|
|
1101
|
-
throw
|
|
1102
|
-
"RILL-R004",
|
|
1103
|
-
`${role} message requires 'content'`
|
|
1104
|
-
);
|
|
1276
|
+
throw haltInvalid(ctx, "INVALID_INPUT", "missing_message_content", `${role} message requires 'content'`);
|
|
1105
1277
|
}
|
|
1106
1278
|
messages.push({
|
|
1107
1279
|
role,
|
|
@@ -1278,8 +1450,8 @@ function createOpenAIExtension(config) {
|
|
|
1278
1450
|
yield chunk;
|
|
1279
1451
|
}
|
|
1280
1452
|
} catch (error) {
|
|
1281
|
-
|
|
1282
|
-
|
|
1453
|
+
if (error instanceof RuntimeHaltSignal3) throw error;
|
|
1454
|
+
throwProviderHalt(ctx, "OpenAI", error, detectOpenAIError);
|
|
1283
1455
|
}
|
|
1284
1456
|
}
|
|
1285
1457
|
const resolve = async () => {
|
|
@@ -1320,14 +1492,23 @@ function createOpenAIExtension(config) {
|
|
|
1320
1492
|
return result;
|
|
1321
1493
|
} catch (error) {
|
|
1322
1494
|
const duration = Date.now() - startTime;
|
|
1323
|
-
|
|
1495
|
+
if (error instanceof RuntimeHaltSignal3) {
|
|
1496
|
+
emitExtensionEvent(ctx, {
|
|
1497
|
+
event: "openai:error",
|
|
1498
|
+
subsystem: "extension:openai",
|
|
1499
|
+
error: getStatus2(error.value).message,
|
|
1500
|
+
duration
|
|
1501
|
+
});
|
|
1502
|
+
throw error;
|
|
1503
|
+
}
|
|
1504
|
+
const invalid = mapProviderError(ctx, "OpenAI", error, detectOpenAIError);
|
|
1324
1505
|
emitExtensionEvent(ctx, {
|
|
1325
1506
|
event: "openai:error",
|
|
1326
1507
|
subsystem: "extension:openai",
|
|
1327
|
-
error:
|
|
1508
|
+
error: getStatus2(invalid).message,
|
|
1328
1509
|
duration
|
|
1329
1510
|
});
|
|
1330
|
-
throw
|
|
1511
|
+
throw new RuntimeHaltSignal3(invalid, true);
|
|
1331
1512
|
}
|
|
1332
1513
|
};
|
|
1333
1514
|
const retType = {
|
|
@@ -1386,16 +1567,10 @@ function createOpenAIExtension(config) {
|
|
|
1386
1567
|
const schemaArg = args["schema"];
|
|
1387
1568
|
const options = args["options"] ?? {};
|
|
1388
1569
|
if (!schemaArg || !schemaArg.__rill_type || !schemaArg.structure) {
|
|
1389
|
-
throw
|
|
1390
|
-
"RILL-R004",
|
|
1391
|
-
"generate requires a type expression as schema"
|
|
1392
|
-
);
|
|
1570
|
+
throw haltInvalid(ctx, "INVALID_INPUT", "invalid_schema", "generate requires a type expression as schema");
|
|
1393
1571
|
}
|
|
1394
1572
|
if (schemaArg.structure.kind !== "dict") {
|
|
1395
|
-
throw
|
|
1396
|
-
"RILL-R004",
|
|
1397
|
-
`generate requires a dict type as schema, got ${schemaArg.structure.kind}`
|
|
1398
|
-
);
|
|
1573
|
+
throw haltInvalid(ctx, "INVALID_INPUT", "invalid_schema_type", `generate requires a dict type as schema, got ${schemaArg.structure.kind}`);
|
|
1399
1574
|
}
|
|
1400
1575
|
const jsonSchema = buildJsonSchemaFromStructuralType(schemaArg.structure);
|
|
1401
1576
|
const system = typeof options["system"] === "string" ? options["system"] : factorySystem;
|
|
@@ -1411,20 +1586,14 @@ function createOpenAIExtension(config) {
|
|
|
1411
1586
|
const prependedMessages = options["messages"];
|
|
1412
1587
|
for (const msg of prependedMessages) {
|
|
1413
1588
|
if (!msg || typeof msg !== "object" || !("role" in msg)) {
|
|
1414
|
-
throw
|
|
1415
|
-
"RILL-R004",
|
|
1416
|
-
"message missing required 'role' field"
|
|
1417
|
-
);
|
|
1589
|
+
throw haltInvalid(ctx, "INVALID_INPUT", "invalid_message_format", "message missing required 'role' field");
|
|
1418
1590
|
}
|
|
1419
1591
|
const role = msg["role"];
|
|
1420
1592
|
if (role !== "user" && role !== "assistant") {
|
|
1421
|
-
throw
|
|
1593
|
+
throw haltInvalid(ctx, "INVALID_INPUT", "invalid_role", `invalid role '${String(role)}'`);
|
|
1422
1594
|
}
|
|
1423
1595
|
if (!("content" in msg) || typeof msg["content"] !== "string") {
|
|
1424
|
-
throw
|
|
1425
|
-
"RILL-R004",
|
|
1426
|
-
`${role} message requires 'content'`
|
|
1427
|
-
);
|
|
1596
|
+
throw haltInvalid(ctx, "INVALID_INPUT", "missing_message_content", `${role} message requires 'content'`);
|
|
1428
1597
|
}
|
|
1429
1598
|
apiMessages.push({
|
|
1430
1599
|
role,
|
|
@@ -1456,10 +1625,7 @@ function createOpenAIExtension(config) {
|
|
|
1456
1625
|
data = JSON.parse(raw);
|
|
1457
1626
|
} catch (parseError) {
|
|
1458
1627
|
const detail = parseError instanceof Error ? parseError.message : String(parseError);
|
|
1459
|
-
throw
|
|
1460
|
-
"RILL-R004",
|
|
1461
|
-
`generate: failed to parse response JSON: ${detail}`
|
|
1462
|
-
);
|
|
1628
|
+
throw haltInvalid(ctx, "PROTOCOL", "json_parse_failed", `generate: failed to parse response JSON: ${detail}`);
|
|
1463
1629
|
}
|
|
1464
1630
|
const result = {
|
|
1465
1631
|
data,
|
|
@@ -1485,14 +1651,32 @@ function createOpenAIExtension(config) {
|
|
|
1485
1651
|
return result;
|
|
1486
1652
|
} catch (error) {
|
|
1487
1653
|
const duration = Date.now() - startTime;
|
|
1488
|
-
|
|
1654
|
+
if (error instanceof RuntimeError5) {
|
|
1655
|
+
emitExtensionEvent(ctx, {
|
|
1656
|
+
event: "openai:error",
|
|
1657
|
+
subsystem: "extension:openai",
|
|
1658
|
+
error: error.message,
|
|
1659
|
+
duration
|
|
1660
|
+
});
|
|
1661
|
+
throw error;
|
|
1662
|
+
}
|
|
1663
|
+
if (error instanceof RuntimeHaltSignal3) {
|
|
1664
|
+
emitExtensionEvent(ctx, {
|
|
1665
|
+
event: "openai:error",
|
|
1666
|
+
subsystem: "extension:openai",
|
|
1667
|
+
error: getStatus2(error.value).message,
|
|
1668
|
+
duration
|
|
1669
|
+
});
|
|
1670
|
+
throw error;
|
|
1671
|
+
}
|
|
1672
|
+
const invalid = mapProviderError(ctx, "OpenAI", error, detectOpenAIError);
|
|
1489
1673
|
emitExtensionEvent(ctx, {
|
|
1490
1674
|
event: "openai:error",
|
|
1491
1675
|
subsystem: "extension:openai",
|
|
1492
|
-
error:
|
|
1676
|
+
error: getStatus2(invalid).message,
|
|
1493
1677
|
duration
|
|
1494
1678
|
});
|
|
1495
|
-
throw
|
|
1679
|
+
throw new RuntimeHaltSignal3(invalid, true);
|
|
1496
1680
|
}
|
|
1497
1681
|
},
|
|
1498
1682
|
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.0",
|
|
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"
|