@rcrsr/rill-ext-gemini 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 +233 -93
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -7,11 +7,13 @@ import {
|
|
|
7
7
|
Type
|
|
8
8
|
} from "@google/genai";
|
|
9
9
|
import {
|
|
10
|
-
RuntimeError as
|
|
10
|
+
RuntimeError as RuntimeError5,
|
|
11
|
+
RuntimeHaltSignal as RuntimeHaltSignal3,
|
|
11
12
|
emitExtensionEvent,
|
|
12
13
|
createRillStream,
|
|
13
14
|
createVector,
|
|
14
15
|
isVector,
|
|
16
|
+
getStatus as getStatus2,
|
|
15
17
|
structureToTypeValue,
|
|
16
18
|
toCallable
|
|
17
19
|
} from "@rcrsr/rill";
|
|
@@ -67,27 +69,133 @@ function validateEmbedModel(model) {
|
|
|
67
69
|
}
|
|
68
70
|
|
|
69
71
|
// ../../shared/ext-llm/dist/errors.js
|
|
70
|
-
import {
|
|
71
|
-
function
|
|
72
|
+
import { RuntimeHaltSignal } from "@rcrsr/rill";
|
|
73
|
+
function atomForStatus(status) {
|
|
74
|
+
if (status === 401)
|
|
75
|
+
return "AUTH";
|
|
76
|
+
if (status === 403)
|
|
77
|
+
return "FORBIDDEN";
|
|
78
|
+
if (status === 404)
|
|
79
|
+
return "NOT_FOUND";
|
|
80
|
+
if (status === 408)
|
|
81
|
+
return "TIMEOUT";
|
|
82
|
+
if (status === 409 || status === 412)
|
|
83
|
+
return "CONFLICT";
|
|
84
|
+
if (status === 429)
|
|
85
|
+
return "RATE_LIMIT";
|
|
86
|
+
if (status === 402)
|
|
87
|
+
return "QUOTA_EXCEEDED";
|
|
88
|
+
if (status >= 500 && status <= 599)
|
|
89
|
+
return "UNAVAILABLE";
|
|
90
|
+
if (status >= 400 && status <= 499)
|
|
91
|
+
return "INVALID_INPUT";
|
|
92
|
+
return "UNAVAILABLE";
|
|
93
|
+
}
|
|
94
|
+
function kindForStatus(status) {
|
|
95
|
+
if (status === 401)
|
|
96
|
+
return "authentication_failed";
|
|
97
|
+
if (status === 403)
|
|
98
|
+
return "forbidden";
|
|
99
|
+
if (status === 404)
|
|
100
|
+
return "not_found";
|
|
101
|
+
if (status === 408)
|
|
102
|
+
return "request_timeout";
|
|
103
|
+
if (status === 409 || status === 412)
|
|
104
|
+
return "conflict";
|
|
105
|
+
if (status === 429)
|
|
106
|
+
return "rate_limit_exceeded";
|
|
107
|
+
if (status === 402)
|
|
108
|
+
return "quota_exceeded";
|
|
109
|
+
if (status >= 500 && status <= 599)
|
|
110
|
+
return "server_error";
|
|
111
|
+
return "http_error";
|
|
112
|
+
}
|
|
113
|
+
function mapProviderError(ctx, provider, error, detect) {
|
|
114
|
+
if (error instanceof RuntimeHaltSignal) {
|
|
115
|
+
return ctx.invalidate(error, {
|
|
116
|
+
code: "TIMEOUT",
|
|
117
|
+
provider,
|
|
118
|
+
raw: { kind: "request_cancelled", message: `${provider}: request cancelled` }
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
122
|
+
return ctx.invalidate(error, {
|
|
123
|
+
code: "TIMEOUT",
|
|
124
|
+
provider,
|
|
125
|
+
raw: {
|
|
126
|
+
kind: "request_timeout",
|
|
127
|
+
message: `${provider} error: ${error.message}`
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
}
|
|
72
131
|
const detected = detect(error);
|
|
73
132
|
if (detected !== null) {
|
|
74
133
|
const { status, message } = detected;
|
|
75
134
|
if (status !== void 0) {
|
|
76
|
-
return
|
|
135
|
+
return ctx.invalidate(error, {
|
|
136
|
+
code: atomForStatus(status),
|
|
137
|
+
provider,
|
|
138
|
+
raw: {
|
|
139
|
+
kind: kindForStatus(status),
|
|
140
|
+
status,
|
|
141
|
+
message: `${provider} API error (HTTP ${status}): ${message}`
|
|
142
|
+
}
|
|
143
|
+
});
|
|
77
144
|
}
|
|
78
|
-
return
|
|
145
|
+
return ctx.invalidate(error, {
|
|
146
|
+
code: "UNAVAILABLE",
|
|
147
|
+
provider,
|
|
148
|
+
raw: {
|
|
149
|
+
kind: "provider_error",
|
|
150
|
+
message: `${provider} API error: ${message}`
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
if (error instanceof TypeError) {
|
|
155
|
+
return ctx.invalidate(error, {
|
|
156
|
+
code: "UNAVAILABLE",
|
|
157
|
+
provider,
|
|
158
|
+
raw: {
|
|
159
|
+
kind: "connection_failed",
|
|
160
|
+
message: `${provider} error: ${error.message}`
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
if (error instanceof SyntaxError) {
|
|
165
|
+
return ctx.invalidate(error, {
|
|
166
|
+
code: "PROTOCOL",
|
|
167
|
+
provider,
|
|
168
|
+
raw: {
|
|
169
|
+
kind: "unexpected_response_format",
|
|
170
|
+
message: `${provider} error: ${error.message}`
|
|
171
|
+
}
|
|
172
|
+
});
|
|
79
173
|
}
|
|
80
174
|
if (error instanceof Error) {
|
|
81
|
-
return
|
|
175
|
+
return ctx.invalidate(error, {
|
|
176
|
+
code: "UNAVAILABLE",
|
|
177
|
+
provider,
|
|
178
|
+
raw: {
|
|
179
|
+
kind: "unknown_error",
|
|
180
|
+
message: `${provider} error: ${error.message}`
|
|
181
|
+
}
|
|
182
|
+
});
|
|
82
183
|
}
|
|
83
|
-
return
|
|
184
|
+
return ctx.invalidate(error, {
|
|
185
|
+
code: "UNAVAILABLE",
|
|
186
|
+
provider,
|
|
187
|
+
raw: {
|
|
188
|
+
kind: "unknown_error",
|
|
189
|
+
message: `${provider} error: Unknown error`
|
|
190
|
+
}
|
|
191
|
+
});
|
|
84
192
|
}
|
|
85
193
|
|
|
86
194
|
// ../../shared/ext-llm/dist/tool-loop.js
|
|
87
|
-
import { invokeCallable, isCallable, isDict, isRuntimeCallable, RuntimeError as
|
|
195
|
+
import { getStatus, invokeCallable, isCallable, isDict, isRuntimeCallable, RuntimeError as RuntimeError3, RuntimeHaltSignal as RuntimeHaltSignal2 } from "@rcrsr/rill";
|
|
88
196
|
|
|
89
197
|
// ../../shared/ext-llm/dist/schema.js
|
|
90
|
-
import { RuntimeError as
|
|
198
|
+
import { RuntimeError as RuntimeError2 } from "@rcrsr/rill";
|
|
91
199
|
var RILL_TYPE_MAP = {
|
|
92
200
|
string: "string",
|
|
93
201
|
number: "number",
|
|
@@ -100,24 +208,24 @@ var RILL_TYPE_MAP = {
|
|
|
100
208
|
function mapRillType(rillType) {
|
|
101
209
|
const jsonType = RILL_TYPE_MAP[rillType];
|
|
102
210
|
if (jsonType === void 0) {
|
|
103
|
-
throw new
|
|
211
|
+
throw new RuntimeError2("RILL-R005", `unsupported type: ${rillType}`);
|
|
104
212
|
}
|
|
105
213
|
return jsonType;
|
|
106
214
|
}
|
|
107
215
|
function buildPropertyFromStructuralType(rillType) {
|
|
108
216
|
if (rillType.kind === "closure" || rillType.kind === "tuple") {
|
|
109
|
-
throw new
|
|
217
|
+
throw new RuntimeError2("RILL-R005", `unsupported type for JSON Schema: ${rillType.kind}`);
|
|
110
218
|
}
|
|
111
219
|
if (rillType.kind === "any") {
|
|
112
220
|
return {};
|
|
113
221
|
}
|
|
114
222
|
if (rillType.kind === "list") {
|
|
115
223
|
const listType = rillType;
|
|
116
|
-
const
|
|
224
|
+
const property2 = { type: "array" };
|
|
117
225
|
if (listType.element !== void 0) {
|
|
118
|
-
|
|
226
|
+
property2.items = buildPropertyFromStructuralType(listType.element);
|
|
119
227
|
}
|
|
120
|
-
return
|
|
228
|
+
return property2;
|
|
121
229
|
}
|
|
122
230
|
if (rillType.kind === "dict") {
|
|
123
231
|
const dictType = rillType;
|
|
@@ -130,9 +238,14 @@ function buildPropertyFromStructuralType(rillType) {
|
|
|
130
238
|
additionalProperties: false
|
|
131
239
|
};
|
|
132
240
|
}
|
|
133
|
-
return { type: "object" };
|
|
241
|
+
return { type: "object", additionalProperties: false };
|
|
242
|
+
}
|
|
243
|
+
const jsonType = mapRillType(rillType.kind);
|
|
244
|
+
const property = { type: jsonType };
|
|
245
|
+
if (jsonType === "object") {
|
|
246
|
+
property.additionalProperties = false;
|
|
134
247
|
}
|
|
135
|
-
return
|
|
248
|
+
return property;
|
|
136
249
|
}
|
|
137
250
|
function buildDictSchema(dictType) {
|
|
138
251
|
const properties = {};
|
|
@@ -156,7 +269,7 @@ function buildJsonSchemaFromStructuralType(type, params) {
|
|
|
156
269
|
return buildDictSchema(type);
|
|
157
270
|
}
|
|
158
271
|
if (type.kind !== "closure") {
|
|
159
|
-
throw new
|
|
272
|
+
throw new RuntimeError2("RILL-R005", `unsupported schema kind: ${type.kind} (expected dict or closure)`);
|
|
160
273
|
}
|
|
161
274
|
const properties = {};
|
|
162
275
|
const required = [];
|
|
@@ -184,30 +297,51 @@ function buildJsonSchemaFromStructuralType(type, params) {
|
|
|
184
297
|
}
|
|
185
298
|
|
|
186
299
|
// ../../shared/ext-llm/dist/tool-loop.js
|
|
300
|
+
function readHaltMessage(halt) {
|
|
301
|
+
const status = getStatus(halt.value);
|
|
302
|
+
if (typeof status.message === "string" && status.message.length > 0) {
|
|
303
|
+
return status.message;
|
|
304
|
+
}
|
|
305
|
+
return halt.message;
|
|
306
|
+
}
|
|
307
|
+
function throwToolLoopHalt(ctx, code, kind, message) {
|
|
308
|
+
if (ctx !== void 0) {
|
|
309
|
+
const hostCtx = ctx;
|
|
310
|
+
if (typeof hostCtx.invalidate === "function") {
|
|
311
|
+
const invalid = hostCtx.invalidate(new Error(message), {
|
|
312
|
+
code,
|
|
313
|
+
provider: "tool_loop",
|
|
314
|
+
raw: { kind, message }
|
|
315
|
+
});
|
|
316
|
+
throw new RuntimeHaltSignal2(invalid, true);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
throw new RuntimeError3("RILL-R005", message);
|
|
320
|
+
}
|
|
187
321
|
async function executeToolCall(toolName, toolInput, tools, context) {
|
|
188
322
|
if (!isDict(tools)) {
|
|
189
|
-
|
|
323
|
+
throwToolLoopHalt(context, "INVALID_INPUT", "tools_not_dict", "tool_loop: tools must be a dict of name \u2192 callable");
|
|
190
324
|
}
|
|
191
325
|
const toolsDict = tools;
|
|
192
326
|
const toolFn = toolsDict[toolName];
|
|
193
327
|
if (toolFn === void 0 || toolFn === null) {
|
|
194
|
-
|
|
328
|
+
throwToolLoopHalt(context, "NOT_FOUND", "unknown_tool", `Unknown tool: ${toolName}`);
|
|
195
329
|
}
|
|
196
330
|
if (!isCallable(toolFn)) {
|
|
197
|
-
|
|
331
|
+
throwToolLoopHalt(context, "INVALID_INPUT", "tool_not_callable", `Invalid tool input for ${toolName}: tool must be callable`);
|
|
198
332
|
}
|
|
199
333
|
if (typeof toolInput !== "object" || toolInput === null) {
|
|
200
|
-
|
|
334
|
+
throwToolLoopHalt(context, "INVALID_INPUT", "invalid_tool_input", `Invalid tool input for ${toolName}: input must be an object`);
|
|
201
335
|
}
|
|
202
336
|
const callable = toolFn;
|
|
203
337
|
if (callable.kind !== "runtime" && callable.kind !== "application" && callable.kind !== "script") {
|
|
204
|
-
|
|
338
|
+
throwToolLoopHalt(context, "INVALID_INPUT", "tool_kind_unsupported", `Invalid tool input for ${toolName}: tool must be application, runtime, or script callable`);
|
|
205
339
|
}
|
|
206
340
|
try {
|
|
207
341
|
const inputDict = toolInput;
|
|
208
342
|
if (callable.kind === "script") {
|
|
209
343
|
if (!context) {
|
|
210
|
-
throw new
|
|
344
|
+
throw new RuntimeError3("RILL-R005", `Invalid tool input for ${toolName}: script callable requires a runtime context`);
|
|
211
345
|
}
|
|
212
346
|
let args;
|
|
213
347
|
if (callable.params && callable.params.length > 0) {
|
|
@@ -223,16 +357,17 @@ async function executeToolCall(toolName, toolInput, tools, context) {
|
|
|
223
357
|
const ctx = context ?? {
|
|
224
358
|
parent: void 0,
|
|
225
359
|
variables: /* @__PURE__ */ new Map(),
|
|
226
|
-
pipeValue: null
|
|
360
|
+
pipeValue: null,
|
|
361
|
+
hostContext: {}
|
|
227
362
|
};
|
|
228
363
|
const result = callable.fn(inputDict, ctx);
|
|
229
364
|
return result instanceof Promise ? await result : result;
|
|
230
365
|
} catch (error) {
|
|
231
|
-
if (error instanceof
|
|
366
|
+
if (error instanceof RuntimeError3) {
|
|
232
367
|
throw error;
|
|
233
368
|
}
|
|
234
369
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
235
|
-
throw new
|
|
370
|
+
throw new RuntimeError3("RILL-R005", `Invalid tool input for ${toolName}: ${message}`);
|
|
236
371
|
}
|
|
237
372
|
}
|
|
238
373
|
function sanitizeToolName(name) {
|
|
@@ -303,19 +438,19 @@ function patchResponseToolCallNames(response, nameMap) {
|
|
|
303
438
|
}
|
|
304
439
|
async function executeToolLoop(messages, tools, maxErrors, callbacks, emitEvent, maxTurns = 10, context, yieldChunk, signal) {
|
|
305
440
|
if (tools === void 0) {
|
|
306
|
-
|
|
441
|
+
throwToolLoopHalt(context, "INVALID_INPUT", "tools_required", "tools parameter is required");
|
|
307
442
|
}
|
|
308
443
|
if (!isDict(tools)) {
|
|
309
|
-
|
|
444
|
+
throwToolLoopHalt(context, "INVALID_INPUT", "tools_not_dict", "tool_loop: tools must be a dict of name \u2192 callable");
|
|
310
445
|
}
|
|
311
446
|
const toolsDict = tools;
|
|
312
447
|
const toolDescriptors = Object.entries(toolsDict).map(([name, fn]) => {
|
|
313
448
|
const fnValue = fn;
|
|
314
449
|
if (isRuntimeCallable(fnValue)) {
|
|
315
|
-
|
|
450
|
+
throwToolLoopHalt(context, "INVALID_INPUT", "builtin_tool_unsupported", `tool_loop: builtin "${name}" cannot be used as a tool \u2014 wrap in a closure`);
|
|
316
451
|
}
|
|
317
452
|
if (!isCallable(fnValue)) {
|
|
318
|
-
|
|
453
|
+
throwToolLoopHalt(context, "INVALID_INPUT", "tool_not_callable", `tool_loop: tool "${name}" is not a callable`);
|
|
319
454
|
}
|
|
320
455
|
const callable = fnValue;
|
|
321
456
|
const description = callable.annotations?.["description"] ?? "";
|
|
@@ -350,7 +485,7 @@ async function executeToolLoop(messages, tools, maxErrors, callbacks, emitEvent,
|
|
|
350
485
|
let turnCount = 0;
|
|
351
486
|
while (turnCount < maxTurns) {
|
|
352
487
|
if (signal?.aborted) {
|
|
353
|
-
|
|
488
|
+
throwToolLoopHalt(context, "TIMEOUT", "tool_loop_cancelled", "tool_loop cancelled");
|
|
354
489
|
}
|
|
355
490
|
turnCount++;
|
|
356
491
|
let response;
|
|
@@ -364,8 +499,18 @@ async function executeToolLoop(messages, tools, maxErrors, callbacks, emitEvent,
|
|
|
364
499
|
response = await callbacks.callAPI(currentMessages, providerTools, signal);
|
|
365
500
|
}
|
|
366
501
|
} catch (error) {
|
|
502
|
+
if (error instanceof RuntimeHaltSignal2) {
|
|
503
|
+
throw error;
|
|
504
|
+
}
|
|
505
|
+
if (context !== void 0 && callbacks.detectError !== void 0) {
|
|
506
|
+
const hostCtx = context;
|
|
507
|
+
if (typeof hostCtx.invalidate === "function") {
|
|
508
|
+
const invalid = mapProviderError(hostCtx, "tool_loop", error, callbacks.detectError);
|
|
509
|
+
throw new RuntimeHaltSignal2(invalid, true);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
367
512
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
368
|
-
throw new
|
|
513
|
+
throw new RuntimeError3("RILL-R005", `Provider API error: ${message}`, void 0, { cause: error });
|
|
369
514
|
}
|
|
370
515
|
if (typeof response === "object" && response !== null && "usage" in response) {
|
|
371
516
|
const usage = response["usage"];
|
|
@@ -422,7 +567,11 @@ async function executeToolLoop(messages, tools, maxErrors, callbacks, emitEvent,
|
|
|
422
567
|
const duration = Date.now() - toolStartTime;
|
|
423
568
|
consecutiveErrors++;
|
|
424
569
|
let originalError;
|
|
425
|
-
if (error instanceof
|
|
570
|
+
if (error instanceof RuntimeHaltSignal2) {
|
|
571
|
+
const haltMessage = readHaltMessage(error);
|
|
572
|
+
const prefix = `Invalid tool input for ${name}: `;
|
|
573
|
+
originalError = haltMessage.startsWith(prefix) ? haltMessage.slice(prefix.length) : haltMessage;
|
|
574
|
+
} else if (error instanceof RuntimeError3) {
|
|
426
575
|
const prefix = `Invalid tool input for ${name}: `;
|
|
427
576
|
if (error.message.startsWith(prefix)) {
|
|
428
577
|
originalError = error.message.slice(prefix.length);
|
|
@@ -447,7 +596,7 @@ async function executeToolLoop(messages, tools, maxErrors, callbacks, emitEvent,
|
|
|
447
596
|
duration
|
|
448
597
|
});
|
|
449
598
|
if (consecutiveErrors >= maxErrors) {
|
|
450
|
-
|
|
599
|
+
throwToolLoopHalt(context, "UNAVAILABLE", "consecutive_tool_errors", `Tool execution failed: ${maxErrors} consecutive errors (last: ${name}: ${originalError})`);
|
|
451
600
|
}
|
|
452
601
|
}
|
|
453
602
|
}
|
|
@@ -474,13 +623,13 @@ function buildResponseMessages(inputMessages, assistantContent) {
|
|
|
474
623
|
}
|
|
475
624
|
|
|
476
625
|
// ../../shared/ext-param/dist/param.js
|
|
477
|
-
import { RuntimeError as
|
|
626
|
+
import { RuntimeError as RuntimeError4 } from "@rcrsr/rill";
|
|
478
627
|
function validateParamName(name) {
|
|
479
628
|
if (name === "") {
|
|
480
|
-
throw new
|
|
629
|
+
throw new RuntimeError4("RILL-R001", "param name must not be empty");
|
|
481
630
|
}
|
|
482
631
|
if (/\s/.test(name)) {
|
|
483
|
-
throw new
|
|
632
|
+
throw new RuntimeError4("RILL-R001", "param name must be a valid identifier");
|
|
484
633
|
}
|
|
485
634
|
}
|
|
486
635
|
function buildAnnotations(desc) {
|
|
@@ -597,6 +746,27 @@ var p = {
|
|
|
597
746
|
|
|
598
747
|
// src/factory.ts
|
|
599
748
|
var DEFAULT_MAX_TOKENS = 8192;
|
|
749
|
+
function mapToHalt(ctx, error) {
|
|
750
|
+
const invalid = mapProviderError(ctx, "Gemini", error, detectGeminiError);
|
|
751
|
+
return new RuntimeHaltSignal3(invalid, true);
|
|
752
|
+
}
|
|
753
|
+
function haltInvalid(ctx, code, rawKind, message) {
|
|
754
|
+
return new RuntimeHaltSignal3(
|
|
755
|
+
ctx.invalidate(new Error(message), {
|
|
756
|
+
code,
|
|
757
|
+
provider: "gemini",
|
|
758
|
+
raw: { kind: rawKind, message }
|
|
759
|
+
}),
|
|
760
|
+
true
|
|
761
|
+
);
|
|
762
|
+
}
|
|
763
|
+
function streamErrorMessage(err) {
|
|
764
|
+
if (err === void 0) return "";
|
|
765
|
+
if (err instanceof RuntimeHaltSignal3) {
|
|
766
|
+
return getStatus2(err.value).message || err.message;
|
|
767
|
+
}
|
|
768
|
+
return err.message;
|
|
769
|
+
}
|
|
600
770
|
var detectGeminiError = (error) => {
|
|
601
771
|
if (error instanceof Error) {
|
|
602
772
|
const message = error.message;
|
|
@@ -685,7 +855,7 @@ function createGeminiExtension(config) {
|
|
|
685
855
|
const text = args["text"];
|
|
686
856
|
const options = args["options"] ?? {};
|
|
687
857
|
if (text.trim().length === 0) {
|
|
688
|
-
throw
|
|
858
|
+
throw haltInvalid(ctx, "INVALID_INPUT", "empty_prompt", "prompt text cannot be empty");
|
|
689
859
|
}
|
|
690
860
|
const system = typeof options["system"] === "string" ? options["system"] : factorySystem;
|
|
691
861
|
const maxTokens = typeof options["max_tokens"] === "number" && options["max_tokens"] > 0 ? options["max_tokens"] : factoryMaxTokens;
|
|
@@ -724,12 +894,12 @@ function createGeminiExtension(config) {
|
|
|
724
894
|
}
|
|
725
895
|
}
|
|
726
896
|
} catch (error) {
|
|
727
|
-
streamError = error instanceof
|
|
897
|
+
streamError = error instanceof RuntimeError5 || error instanceof RuntimeHaltSignal3 ? error : mapToHalt(ctx, error);
|
|
728
898
|
const duration = Date.now() - messageStartTime;
|
|
729
899
|
emitExtensionEvent(ctx, {
|
|
730
900
|
event: "gemini:error",
|
|
731
901
|
subsystem: "extension:gemini",
|
|
732
|
-
error: streamError
|
|
902
|
+
error: streamErrorMessage(streamError),
|
|
733
903
|
duration
|
|
734
904
|
});
|
|
735
905
|
throw streamError;
|
|
@@ -820,10 +990,7 @@ function createGeminiExtension(config) {
|
|
|
820
990
|
const inputMessages = args["messages"];
|
|
821
991
|
const options = args["options"] ?? {};
|
|
822
992
|
if (inputMessages.length === 0) {
|
|
823
|
-
throw
|
|
824
|
-
"RILL-R004",
|
|
825
|
-
"messages list cannot be empty"
|
|
826
|
-
);
|
|
993
|
+
throw haltInvalid(ctx, "INVALID_INPUT", "empty_messages", "messages list cannot be empty");
|
|
827
994
|
}
|
|
828
995
|
const system = typeof options["system"] === "string" ? options["system"] : factorySystem;
|
|
829
996
|
const maxTokens = typeof options["max_tokens"] === "number" && options["max_tokens"] > 0 ? options["max_tokens"] : factoryMaxTokens;
|
|
@@ -831,21 +998,15 @@ function createGeminiExtension(config) {
|
|
|
831
998
|
for (let i = 0; i < inputMessages.length; i++) {
|
|
832
999
|
const msg = inputMessages[i];
|
|
833
1000
|
if (!msg || typeof msg !== "object" || !("role" in msg)) {
|
|
834
|
-
throw
|
|
835
|
-
"RILL-R004",
|
|
836
|
-
"message missing required 'role' field"
|
|
837
|
-
);
|
|
1001
|
+
throw haltInvalid(ctx, "INVALID_INPUT", "invalid_message_format", "message missing required 'role' field");
|
|
838
1002
|
}
|
|
839
1003
|
const role = msg["role"];
|
|
840
1004
|
if (role !== "user" && role !== "assistant" && role !== "tool") {
|
|
841
|
-
throw
|
|
1005
|
+
throw haltInvalid(ctx, "INVALID_INPUT", "invalid_role", `invalid role '${String(role)}'`);
|
|
842
1006
|
}
|
|
843
1007
|
if (role === "user" || role === "tool") {
|
|
844
1008
|
if (!("content" in msg) || typeof msg["content"] !== "string") {
|
|
845
|
-
throw
|
|
846
|
-
"RILL-R004",
|
|
847
|
-
`${role} message requires 'content'`
|
|
848
|
-
);
|
|
1009
|
+
throw haltInvalid(ctx, "INVALID_INPUT", "missing_message_content", `${role} message requires 'content'`);
|
|
849
1010
|
}
|
|
850
1011
|
contents.push({
|
|
851
1012
|
role: "user",
|
|
@@ -855,10 +1016,7 @@ function createGeminiExtension(config) {
|
|
|
855
1016
|
const hasContent = "content" in msg && msg["content"];
|
|
856
1017
|
const hasToolCalls = "tool_calls" in msg && msg["tool_calls"];
|
|
857
1018
|
if (!hasContent && !hasToolCalls) {
|
|
858
|
-
throw
|
|
859
|
-
"RILL-R004",
|
|
860
|
-
"assistant message requires 'content' or 'tool_calls'"
|
|
861
|
-
);
|
|
1019
|
+
throw haltInvalid(ctx, "INVALID_INPUT", "invalid_assistant_message", "assistant message requires 'content' or 'tool_calls'");
|
|
862
1020
|
}
|
|
863
1021
|
if (hasContent) {
|
|
864
1022
|
contents.push({
|
|
@@ -897,12 +1055,12 @@ function createGeminiExtension(config) {
|
|
|
897
1055
|
}
|
|
898
1056
|
}
|
|
899
1057
|
} catch (error) {
|
|
900
|
-
streamError = error instanceof
|
|
1058
|
+
streamError = error instanceof RuntimeError5 || error instanceof RuntimeHaltSignal3 ? error : mapToHalt(ctx, error);
|
|
901
1059
|
const duration = Date.now() - messagesStartTime;
|
|
902
1060
|
emitExtensionEvent(ctx, {
|
|
903
1061
|
event: "gemini:error",
|
|
904
1062
|
subsystem: "extension:gemini",
|
|
905
|
-
error: streamError
|
|
1063
|
+
error: streamErrorMessage(streamError),
|
|
906
1064
|
duration
|
|
907
1065
|
});
|
|
908
1066
|
throw streamError;
|
|
@@ -995,10 +1153,7 @@ function createGeminiExtension(config) {
|
|
|
995
1153
|
});
|
|
996
1154
|
const embedding = response.embeddings?.[0];
|
|
997
1155
|
if (!embedding || !embedding.values || embedding.values.length === 0) {
|
|
998
|
-
throw
|
|
999
|
-
"RILL-R004",
|
|
1000
|
-
"Gemini: empty embedding returned"
|
|
1001
|
-
);
|
|
1156
|
+
throw haltInvalid(ctx, "PROTOCOL", "empty_embedding_response", "Gemini: empty embedding returned");
|
|
1002
1157
|
}
|
|
1003
1158
|
const float32Data = new Float32Array(embedding.values);
|
|
1004
1159
|
const vector = createVector(float32Data, factoryEmbedModel);
|
|
@@ -1013,11 +1168,11 @@ function createGeminiExtension(config) {
|
|
|
1013
1168
|
return vector;
|
|
1014
1169
|
} catch (error) {
|
|
1015
1170
|
const duration = Date.now() - startTime;
|
|
1016
|
-
const rillError = error instanceof
|
|
1171
|
+
const rillError = error instanceof RuntimeError5 || error instanceof RuntimeHaltSignal3 ? error : mapToHalt(ctx, error);
|
|
1017
1172
|
emitExtensionEvent(ctx, {
|
|
1018
1173
|
event: "gemini:error",
|
|
1019
1174
|
subsystem: "extension:gemini",
|
|
1020
|
-
error: rillError
|
|
1175
|
+
error: streamErrorMessage(rillError),
|
|
1021
1176
|
duration
|
|
1022
1177
|
});
|
|
1023
1178
|
throw rillError;
|
|
@@ -1044,17 +1199,11 @@ function createGeminiExtension(config) {
|
|
|
1044
1199
|
});
|
|
1045
1200
|
const vectors = [];
|
|
1046
1201
|
if (!response.embeddings || response.embeddings.length === 0) {
|
|
1047
|
-
throw
|
|
1048
|
-
"RILL-R004",
|
|
1049
|
-
"Gemini: empty embeddings returned"
|
|
1050
|
-
);
|
|
1202
|
+
throw haltInvalid(ctx, "PROTOCOL", "empty_embeddings_response", "Gemini: empty embeddings returned");
|
|
1051
1203
|
}
|
|
1052
1204
|
for (const embedding of response.embeddings) {
|
|
1053
1205
|
if (!embedding || !embedding.values || embedding.values.length === 0) {
|
|
1054
|
-
throw
|
|
1055
|
-
"RILL-R004",
|
|
1056
|
-
"Gemini: empty embedding returned"
|
|
1057
|
-
);
|
|
1206
|
+
throw haltInvalid(ctx, "PROTOCOL", "empty_embedding_response", "Gemini: empty embedding returned");
|
|
1058
1207
|
}
|
|
1059
1208
|
const float32Data = new Float32Array(embedding.values);
|
|
1060
1209
|
const vector = createVector(float32Data, factoryEmbedModel);
|
|
@@ -1074,11 +1223,11 @@ function createGeminiExtension(config) {
|
|
|
1074
1223
|
return vectors;
|
|
1075
1224
|
} catch (error) {
|
|
1076
1225
|
const duration = Date.now() - startTime;
|
|
1077
|
-
const rillError = error instanceof
|
|
1226
|
+
const rillError = error instanceof RuntimeError5 || error instanceof RuntimeHaltSignal3 ? error : mapToHalt(ctx, error);
|
|
1078
1227
|
emitExtensionEvent(ctx, {
|
|
1079
1228
|
event: "gemini:error",
|
|
1080
1229
|
subsystem: "extension:gemini",
|
|
1081
|
-
error: rillError
|
|
1230
|
+
error: streamErrorMessage(rillError),
|
|
1082
1231
|
duration
|
|
1083
1232
|
});
|
|
1084
1233
|
throw rillError;
|
|
@@ -1110,7 +1259,7 @@ function createGeminiExtension(config) {
|
|
|
1110
1259
|
const toolsDict = args["tools"];
|
|
1111
1260
|
const options = args["options"] ?? {};
|
|
1112
1261
|
if (prompt.trim().length === 0) {
|
|
1113
|
-
throw
|
|
1262
|
+
throw haltInvalid(ctx, "INVALID_INPUT", "empty_prompt", "prompt text cannot be empty");
|
|
1114
1263
|
}
|
|
1115
1264
|
const system = typeof options["system"] === "string" ? options["system"] : factorySystem;
|
|
1116
1265
|
const maxTokens = typeof options["max_tokens"] === "number" && options["max_tokens"] > 0 ? options["max_tokens"] : factoryMaxTokens;
|
|
@@ -1309,7 +1458,7 @@ function createGeminiExtension(config) {
|
|
|
1309
1458
|
r();
|
|
1310
1459
|
}
|
|
1311
1460
|
}).catch((error) => {
|
|
1312
|
-
streamError = error instanceof
|
|
1461
|
+
streamError = error instanceof RuntimeError5 || error instanceof RuntimeHaltSignal3 ? error : mapToHalt(ctx, error);
|
|
1313
1462
|
streamDone = true;
|
|
1314
1463
|
if (resolveNext) {
|
|
1315
1464
|
const r = resolveNext;
|
|
@@ -1358,7 +1507,7 @@ function createGeminiExtension(config) {
|
|
|
1358
1507
|
emitExtensionEvent(ctx, {
|
|
1359
1508
|
event: "gemini:error",
|
|
1360
1509
|
subsystem: "extension:gemini",
|
|
1361
|
-
error: streamError
|
|
1510
|
+
error: streamErrorMessage(streamError),
|
|
1362
1511
|
duration: Date.now()
|
|
1363
1512
|
});
|
|
1364
1513
|
return {
|
|
@@ -1374,7 +1523,7 @@ function createGeminiExtension(config) {
|
|
|
1374
1523
|
emitExtensionEvent(ctx, {
|
|
1375
1524
|
event: "gemini:error",
|
|
1376
1525
|
subsystem: "extension:gemini",
|
|
1377
|
-
error: streamError
|
|
1526
|
+
error: streamErrorMessage(streamError),
|
|
1378
1527
|
duration
|
|
1379
1528
|
});
|
|
1380
1529
|
throw streamError;
|
|
@@ -1447,16 +1596,10 @@ function createGeminiExtension(config) {
|
|
|
1447
1596
|
const schemaArg = args["schema"];
|
|
1448
1597
|
const options = args["options"] ?? {};
|
|
1449
1598
|
if (!schemaArg || !schemaArg.__rill_type || !schemaArg.structure) {
|
|
1450
|
-
throw
|
|
1451
|
-
"RILL-R004",
|
|
1452
|
-
"generate requires a type expression as schema"
|
|
1453
|
-
);
|
|
1599
|
+
throw haltInvalid(ctx, "INVALID_INPUT", "invalid_schema", "generate requires a type expression as schema");
|
|
1454
1600
|
}
|
|
1455
1601
|
if (schemaArg.structure.kind !== "dict") {
|
|
1456
|
-
throw
|
|
1457
|
-
"RILL-R004",
|
|
1458
|
-
`generate requires a dict type as schema, got ${schemaArg.structure.kind}`
|
|
1459
|
-
);
|
|
1602
|
+
throw haltInvalid(ctx, "INVALID_INPUT", "invalid_schema_type", `generate requires a dict type as schema, got ${schemaArg.structure.kind}`);
|
|
1460
1603
|
}
|
|
1461
1604
|
const jsonSchema = buildJsonSchemaFromStructuralType(schemaArg.structure);
|
|
1462
1605
|
const geminiProperties = {};
|
|
@@ -1518,10 +1661,7 @@ function createGeminiExtension(config) {
|
|
|
1518
1661
|
data = JSON.parse(raw);
|
|
1519
1662
|
} catch (parseError) {
|
|
1520
1663
|
const detail = parseError instanceof Error ? parseError.message : String(parseError);
|
|
1521
|
-
throw
|
|
1522
|
-
"RILL-R004",
|
|
1523
|
-
`generate: failed to parse response JSON: ${detail}`
|
|
1524
|
-
);
|
|
1664
|
+
throw haltInvalid(ctx, "PROTOCOL", "json_parse_failed", `generate: failed to parse response JSON: ${detail}`);
|
|
1525
1665
|
}
|
|
1526
1666
|
const inputTokens = response.usageMetadata?.promptTokenCount ?? 0;
|
|
1527
1667
|
const outputTokens = response.usageMetadata?.candidatesTokenCount ?? 0;
|
|
@@ -1551,11 +1691,11 @@ function createGeminiExtension(config) {
|
|
|
1551
1691
|
return generateResult;
|
|
1552
1692
|
} catch (error) {
|
|
1553
1693
|
const duration = Date.now() - startTime;
|
|
1554
|
-
const rillError = error instanceof
|
|
1694
|
+
const rillError = error instanceof RuntimeError5 || error instanceof RuntimeHaltSignal3 ? error : mapToHalt(ctx, error);
|
|
1555
1695
|
emitExtensionEvent(ctx, {
|
|
1556
1696
|
event: "gemini:error",
|
|
1557
1697
|
subsystem: "extension:gemini",
|
|
1558
|
-
error: rillError
|
|
1698
|
+
error: streamErrorMessage(rillError),
|
|
1559
1699
|
duration
|
|
1560
1700
|
});
|
|
1561
1701
|
throw rillError;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rcrsr/rill-ext-gemini",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.1",
|
|
4
4
|
"description": "rill extension for Google Gemini 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"
|