@rcrsr/rill-ext-gemini 0.8.5 → 0.9.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/README.md +7 -123
- package/dist/index.d.ts +2 -6
- package/dist/index.js +440 -102
- package/package.json +11 -11
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @rcrsr/rill-ext-gemini
|
|
2
2
|
|
|
3
|
-
[rill](https://rill.run) extension for [Google Gemini](https://ai.google.dev/docs) API integration. Provides `message`, `messages`, `embed`, `embed_batch`, and `
|
|
3
|
+
[rill](https://rill.run) extension for [Google Gemini](https://ai.google.dev/docs) API integration. Provides `message`, `messages`, `embed`, `embed_batch`, `tool_loop`, and `generate` host functions.
|
|
4
4
|
|
|
5
5
|
> **Experimental.** Breaking changes will occur before stabilization.
|
|
6
6
|
|
|
@@ -36,131 +36,15 @@ const result = await execute(parse(script), ctx);
|
|
|
36
36
|
dispose?.();
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
-
##
|
|
40
|
-
|
|
41
|
-
All functions return a dict with `content`, `model`, `usage`, `stop_reason`, `id`, and `messages`.
|
|
42
|
-
|
|
43
|
-
### gemini::message(text, options?)
|
|
44
|
-
|
|
45
|
-
Send a single message to Gemini.
|
|
46
|
-
|
|
47
|
-
```rill
|
|
48
|
-
gemini::message("Analyze this code for security issues") => $response
|
|
49
|
-
$response.content -> log
|
|
50
|
-
$response.usage.output -> log
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
### gemini::messages(messages, options?)
|
|
54
|
-
|
|
55
|
-
Send a multi-turn conversation.
|
|
56
|
-
|
|
57
|
-
```rill
|
|
58
|
-
gemini::messages([
|
|
59
|
-
[role: "user", content: "What is rill?"],
|
|
60
|
-
[role: "assistant", content: "A scripting language for AI agents."],
|
|
61
|
-
[role: "user", content: "Show me an example."]
|
|
62
|
-
]) => $response
|
|
63
|
-
$response.content -> log
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
### gemini::embed(text)
|
|
67
|
-
|
|
68
|
-
Generate an embedding vector for text. Requires `embed_model` in config.
|
|
69
|
-
|
|
70
|
-
```rill
|
|
71
|
-
gemini::embed("Hello world") => $vector
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
### gemini::embed_batch(texts)
|
|
75
|
-
|
|
76
|
-
Generate embedding vectors for multiple texts in a single API call.
|
|
77
|
-
|
|
78
|
-
```rill
|
|
79
|
-
gemini::embed_batch(["Hello", "World"]) => $vectors
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
### gemini::tool_loop(prompt, options)
|
|
83
|
-
|
|
84
|
-
Execute a tool-use loop where the model calls rill functions iteratively.
|
|
85
|
-
|
|
86
|
-
```rill
|
|
87
|
-
gemini::tool_loop("Find the weather", [tools: $my_tools]) => $result
|
|
88
|
-
$result.content -> log
|
|
89
|
-
$result.turns -> log
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
## Configuration
|
|
93
|
-
|
|
94
|
-
```typescript
|
|
95
|
-
const ext = createGeminiExtension({
|
|
96
|
-
api_key: process.env.GOOGLE_API_KEY!,
|
|
97
|
-
model: 'gemini-2.0-flash',
|
|
98
|
-
temperature: 0.7,
|
|
99
|
-
max_tokens: 8192,
|
|
100
|
-
system: 'You are a helpful assistant.',
|
|
101
|
-
embed_model: 'text-embedding-004',
|
|
102
|
-
});
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
| Option | Type | Default | Description |
|
|
106
|
-
|--------|------|---------|-------------|
|
|
107
|
-
| `api_key` | string | required | Google API key |
|
|
108
|
-
| `model` | string | required | Model identifier |
|
|
109
|
-
| `temperature` | number | undefined | Temperature (0.0-2.0) |
|
|
110
|
-
| `base_url` | string | undefined | Custom API endpoint URL |
|
|
111
|
-
| `max_tokens` | number | `8192` | Max tokens in response |
|
|
112
|
-
| `max_retries` | number | undefined | Max retry attempts |
|
|
113
|
-
| `timeout` | number | undefined | Request timeout in ms |
|
|
114
|
-
| `system` | string | undefined | Default system instruction |
|
|
115
|
-
| `embed_model` | string | undefined | Embedding model identifier |
|
|
116
|
-
|
|
117
|
-
## Result Shape
|
|
118
|
-
|
|
119
|
-
```typescript
|
|
120
|
-
interface GeminiResult {
|
|
121
|
-
content: string; // response text
|
|
122
|
-
model: string; // model used
|
|
123
|
-
usage: {
|
|
124
|
-
input: number; // prompt tokens
|
|
125
|
-
output: number; // completion tokens
|
|
126
|
-
};
|
|
127
|
-
stop_reason: string; // finish reason
|
|
128
|
-
id: string; // request ID
|
|
129
|
-
messages: Array<{ // full conversation history
|
|
130
|
-
role: string;
|
|
131
|
-
content: string;
|
|
132
|
-
}>;
|
|
133
|
-
}
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
## Lifecycle
|
|
137
|
-
|
|
138
|
-
Call `dispose()` on the extension to cancel pending requests:
|
|
139
|
-
|
|
140
|
-
```typescript
|
|
141
|
-
const ext = createGeminiExtension({ ... });
|
|
142
|
-
// ... use extension ...
|
|
143
|
-
await ext.dispose?.();
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
## Test Host
|
|
147
|
-
|
|
148
|
-
A runnable example at `examples/test-host.ts` wires up the extension with the rill runtime:
|
|
149
|
-
|
|
150
|
-
```bash
|
|
151
|
-
pnpm exec tsx examples/test-host.ts
|
|
152
|
-
pnpm exec tsx examples/test-host.ts -e 'gemini::message("Tell me a joke") -> log'
|
|
153
|
-
pnpm exec tsx examples/test-host.ts script.rill
|
|
154
|
-
```
|
|
39
|
+
## Documentation
|
|
155
40
|
|
|
156
|
-
|
|
41
|
+
See [full documentation](docs/extension-llm-gemini.md) for configuration, functions, error handling, events, and examples.
|
|
157
42
|
|
|
158
|
-
##
|
|
43
|
+
## Related
|
|
159
44
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
| [Host API Reference](https://github.com/rcrsr/rill/blob/main/docs/ref-host-api.md) | Runtime context and host functions |
|
|
45
|
+
- [rill](https://github.com/rcrsr/rill) — Core language runtime
|
|
46
|
+
- [Extensions Guide](https://github.com/rcrsr/rill/blob/main/docs/integration-extensions.md) — Extension contract and patterns
|
|
47
|
+
- [Host API Reference](https://github.com/rcrsr/rill/blob/main/docs/ref-host-api.md) — Runtime context and host functions
|
|
164
48
|
|
|
165
49
|
## License
|
|
166
50
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Generated by dts-bundle-generator v9.5.1
|
|
2
2
|
|
|
3
|
-
import { ExtensionResult } from '@rcrsr/rill';
|
|
3
|
+
import { ExtensionConfigSchema, ExtensionResult } from '@rcrsr/rill';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Base configuration for LLM extensions
|
|
@@ -73,12 +73,8 @@ export type GeminiExtensionConfig = LLMProviderConfig;
|
|
|
73
73
|
* ```
|
|
74
74
|
*/
|
|
75
75
|
export declare function createGeminiExtension(config: GeminiExtensionConfig): ExtensionResult;
|
|
76
|
-
/**
|
|
77
|
-
* @rcrsr/rill-ext-gemini
|
|
78
|
-
*
|
|
79
|
-
* Extension for Google Gemini API integration with rill scripts.
|
|
80
|
-
*/
|
|
81
76
|
export declare const VERSION = "0.0.1";
|
|
77
|
+
export declare const configSchema: ExtensionConfigSchema;
|
|
82
78
|
|
|
83
79
|
export {
|
|
84
80
|
LLMProviderConfig as LLMExtensionConfig,
|
package/dist/index.js
CHANGED
|
@@ -4,11 +4,11 @@ import {
|
|
|
4
4
|
Type
|
|
5
5
|
} from "@google/genai";
|
|
6
6
|
import {
|
|
7
|
-
RuntimeError as
|
|
7
|
+
RuntimeError as RuntimeError5,
|
|
8
8
|
emitExtensionEvent,
|
|
9
9
|
createVector,
|
|
10
10
|
isVector,
|
|
11
|
-
|
|
11
|
+
isDict as isDict2
|
|
12
12
|
} from "@rcrsr/rill";
|
|
13
13
|
|
|
14
14
|
// ../../shared/ext-llm/dist/validation.js
|
|
@@ -79,29 +79,108 @@ function mapProviderError(providerName, error, detect) {
|
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
// ../../shared/ext-llm/dist/tool-loop.js
|
|
82
|
-
import { isCallable, isDict, RuntimeError as
|
|
82
|
+
import { invokeCallable, isCallable, isDict, isRuntimeCallable, RuntimeError as RuntimeError4 } from "@rcrsr/rill";
|
|
83
|
+
|
|
84
|
+
// ../../shared/ext-llm/dist/schema.js
|
|
85
|
+
import { RuntimeError as RuntimeError3 } from "@rcrsr/rill";
|
|
86
|
+
var RILL_TYPE_MAP = {
|
|
87
|
+
string: "string",
|
|
88
|
+
number: "number",
|
|
89
|
+
bool: "boolean",
|
|
90
|
+
list: "array",
|
|
91
|
+
dict: "object",
|
|
92
|
+
vector: "object",
|
|
93
|
+
shape: "object"
|
|
94
|
+
};
|
|
95
|
+
function mapRillType(rillType) {
|
|
96
|
+
const jsonType = RILL_TYPE_MAP[rillType];
|
|
97
|
+
if (jsonType === void 0) {
|
|
98
|
+
throw new RuntimeError3("RILL-R004", `unsupported type: ${rillType}`);
|
|
99
|
+
}
|
|
100
|
+
return jsonType;
|
|
101
|
+
}
|
|
102
|
+
function buildJsonSchema(rillSchema) {
|
|
103
|
+
const properties = {};
|
|
104
|
+
const required = [];
|
|
105
|
+
for (const [key, value] of Object.entries(rillSchema)) {
|
|
106
|
+
if (typeof value === "string") {
|
|
107
|
+
properties[key] = buildProperty(value);
|
|
108
|
+
} else if (typeof value === "object" && value !== null) {
|
|
109
|
+
properties[key] = buildProperty(value);
|
|
110
|
+
} else {
|
|
111
|
+
throw new RuntimeError3("RILL-R004", `unsupported type: ${String(value)}`);
|
|
112
|
+
}
|
|
113
|
+
required.push(key);
|
|
114
|
+
}
|
|
115
|
+
return { type: "object", properties, required, additionalProperties: false };
|
|
116
|
+
}
|
|
117
|
+
function buildProperty(descriptor) {
|
|
118
|
+
if (typeof descriptor === "string") {
|
|
119
|
+
const jsonType2 = mapRillType(descriptor);
|
|
120
|
+
return { type: jsonType2 };
|
|
121
|
+
}
|
|
122
|
+
const rillType = descriptor["type"];
|
|
123
|
+
if (typeof rillType !== "string") {
|
|
124
|
+
throw new RuntimeError3("RILL-R004", `unsupported type: ${String(rillType)}`);
|
|
125
|
+
}
|
|
126
|
+
const jsonType = mapRillType(rillType);
|
|
127
|
+
const property = { type: jsonType };
|
|
128
|
+
const description = descriptor["description"];
|
|
129
|
+
if (typeof description === "string") {
|
|
130
|
+
property.description = description;
|
|
131
|
+
}
|
|
132
|
+
if ("enum" in descriptor) {
|
|
133
|
+
if (rillType !== "string") {
|
|
134
|
+
throw new RuntimeError3("RILL-R004", "enum is only valid for string type");
|
|
135
|
+
}
|
|
136
|
+
const enumValues = descriptor["enum"];
|
|
137
|
+
if (Array.isArray(enumValues)) {
|
|
138
|
+
property.enum = enumValues;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (rillType === "list" && "items" in descriptor) {
|
|
142
|
+
const items = descriptor["items"];
|
|
143
|
+
if (typeof items === "string") {
|
|
144
|
+
property.items = buildProperty(items);
|
|
145
|
+
} else if (typeof items === "object" && items !== null) {
|
|
146
|
+
property.items = buildProperty(items);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (rillType === "dict" && "properties" in descriptor) {
|
|
150
|
+
const nestedProps = descriptor["properties"];
|
|
151
|
+
if (typeof nestedProps === "object" && nestedProps !== null) {
|
|
152
|
+
const subSchema = buildJsonSchema(nestedProps);
|
|
153
|
+
property.properties = subSchema.properties;
|
|
154
|
+
property.required = subSchema.required;
|
|
155
|
+
property.additionalProperties = false;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return property;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// ../../shared/ext-llm/dist/tool-loop.js
|
|
83
162
|
async function executeToolCall(toolName, toolInput, tools, context) {
|
|
84
163
|
if (!isDict(tools)) {
|
|
85
|
-
throw new
|
|
164
|
+
throw new RuntimeError4("RILL-R004", "tool_loop: tools must be a dict of name \u2192 callable");
|
|
86
165
|
}
|
|
87
166
|
const toolsDict = tools;
|
|
88
167
|
const toolFn = toolsDict[toolName];
|
|
89
168
|
if (toolFn === void 0 || toolFn === null) {
|
|
90
|
-
throw new
|
|
169
|
+
throw new RuntimeError4("RILL-R004", `Unknown tool: ${toolName}`);
|
|
91
170
|
}
|
|
92
171
|
if (!isCallable(toolFn)) {
|
|
93
|
-
throw new
|
|
172
|
+
throw new RuntimeError4("RILL-R004", `Invalid tool input for ${toolName}: tool must be callable`);
|
|
94
173
|
}
|
|
95
174
|
if (typeof toolInput !== "object" || toolInput === null) {
|
|
96
|
-
throw new
|
|
175
|
+
throw new RuntimeError4("RILL-R004", `Invalid tool input for ${toolName}: input must be an object`);
|
|
97
176
|
}
|
|
98
177
|
const callable = toolFn;
|
|
99
|
-
if (callable.kind !== "runtime" && callable.kind !== "application") {
|
|
100
|
-
throw new
|
|
178
|
+
if (callable.kind !== "runtime" && callable.kind !== "application" && callable.kind !== "script") {
|
|
179
|
+
throw new RuntimeError4("RILL-R004", `Invalid tool input for ${toolName}: tool must be application, runtime, or script callable`);
|
|
101
180
|
}
|
|
102
181
|
try {
|
|
103
182
|
let args;
|
|
104
|
-
if (callable.kind === "application" && callable.params) {
|
|
183
|
+
if ((callable.kind === "application" || callable.kind === "script") && callable.params && callable.params.length > 0) {
|
|
105
184
|
const params = callable.params;
|
|
106
185
|
const inputDict = toolInput;
|
|
107
186
|
args = params.map((param) => {
|
|
@@ -111,6 +190,12 @@ async function executeToolCall(toolName, toolInput, tools, context) {
|
|
|
111
190
|
} else {
|
|
112
191
|
args = [toolInput];
|
|
113
192
|
}
|
|
193
|
+
if (callable.kind === "script") {
|
|
194
|
+
if (!context) {
|
|
195
|
+
throw new RuntimeError4("RILL-R004", `Invalid tool input for ${toolName}: script callable requires a runtime context`);
|
|
196
|
+
}
|
|
197
|
+
return await invokeCallable(callable, args, context);
|
|
198
|
+
}
|
|
114
199
|
const ctx = context ?? {
|
|
115
200
|
parent: void 0,
|
|
116
201
|
variables: /* @__PURE__ */ new Map(),
|
|
@@ -119,74 +204,146 @@ async function executeToolCall(toolName, toolInput, tools, context) {
|
|
|
119
204
|
const result = callable.fn(args, ctx);
|
|
120
205
|
return result instanceof Promise ? await result : result;
|
|
121
206
|
} catch (error) {
|
|
122
|
-
if (error instanceof
|
|
207
|
+
if (error instanceof RuntimeError4) {
|
|
123
208
|
throw error;
|
|
124
209
|
}
|
|
125
210
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
126
|
-
throw new
|
|
211
|
+
throw new RuntimeError4("RILL-R004", `Invalid tool input for ${toolName}: ${message}`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
function sanitizeToolName(name) {
|
|
215
|
+
const match = name.match(/^[a-zA-Z0-9_-]*/);
|
|
216
|
+
const sanitized = match ? match[0] : "";
|
|
217
|
+
return sanitized.length > 0 ? sanitized : name;
|
|
218
|
+
}
|
|
219
|
+
function patchResponseToolCallNames(response, nameMap) {
|
|
220
|
+
if (!nameMap.size || !response || typeof response !== "object")
|
|
221
|
+
return;
|
|
222
|
+
const resp = response;
|
|
223
|
+
if (Array.isArray(resp["choices"])) {
|
|
224
|
+
for (const choice of resp["choices"]) {
|
|
225
|
+
const msg = choice?.["message"];
|
|
226
|
+
const tcs = msg?.["tool_calls"];
|
|
227
|
+
if (Array.isArray(tcs)) {
|
|
228
|
+
for (const tc of tcs) {
|
|
229
|
+
const fn = tc?.["function"];
|
|
230
|
+
if (fn && typeof fn["name"] === "string") {
|
|
231
|
+
const orig = fn["name"];
|
|
232
|
+
const san = nameMap.get(orig);
|
|
233
|
+
if (san !== void 0)
|
|
234
|
+
fn["name"] = san;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
if (Array.isArray(resp["content"])) {
|
|
241
|
+
for (const block of resp["content"]) {
|
|
242
|
+
const b = block;
|
|
243
|
+
if (b?.["type"] === "tool_use" && typeof b?.["name"] === "string") {
|
|
244
|
+
const orig = b["name"];
|
|
245
|
+
const san = nameMap.get(orig);
|
|
246
|
+
if (san !== void 0)
|
|
247
|
+
b["name"] = san;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
if (Array.isArray(resp["functionCalls"])) {
|
|
252
|
+
for (const fc of resp["functionCalls"]) {
|
|
253
|
+
const f = fc;
|
|
254
|
+
if (typeof f?.["name"] === "string") {
|
|
255
|
+
const orig = f["name"];
|
|
256
|
+
const san = nameMap.get(orig);
|
|
257
|
+
if (san !== void 0)
|
|
258
|
+
f["name"] = san;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
if (Array.isArray(resp["candidates"])) {
|
|
263
|
+
for (const cand of resp["candidates"]) {
|
|
264
|
+
const content = cand?.["content"];
|
|
265
|
+
const parts = content?.["parts"];
|
|
266
|
+
if (Array.isArray(parts)) {
|
|
267
|
+
for (const part of parts) {
|
|
268
|
+
const fc = part?.["functionCall"];
|
|
269
|
+
if (fc && typeof fc["name"] === "string") {
|
|
270
|
+
const orig = fc["name"];
|
|
271
|
+
const san = nameMap.get(orig);
|
|
272
|
+
if (san !== void 0)
|
|
273
|
+
fc["name"] = san;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
127
278
|
}
|
|
128
279
|
}
|
|
129
280
|
async function executeToolLoop(messages, tools, maxErrors, callbacks, emitEvent, maxTurns = 10, context) {
|
|
130
281
|
if (tools === void 0) {
|
|
131
|
-
throw new
|
|
282
|
+
throw new RuntimeError4("RILL-R004", "tools parameter is required");
|
|
132
283
|
}
|
|
133
284
|
if (!isDict(tools)) {
|
|
134
|
-
throw new
|
|
285
|
+
throw new RuntimeError4("RILL-R004", "tool_loop: tools must be a dict of name \u2192 callable");
|
|
135
286
|
}
|
|
136
287
|
const toolsDict = tools;
|
|
137
288
|
const toolDescriptors = Object.entries(toolsDict).map(([name, fn]) => {
|
|
138
289
|
const fnValue = fn;
|
|
290
|
+
if (isRuntimeCallable(fnValue)) {
|
|
291
|
+
throw new RuntimeError4("RILL-R004", `tool_loop: builtin "${name}" cannot be used as a tool \u2014 wrap in a closure`);
|
|
292
|
+
}
|
|
139
293
|
if (!isCallable(fnValue)) {
|
|
140
|
-
throw new
|
|
294
|
+
throw new RuntimeError4("RILL-R004", `tool_loop: tool "${name}" is not a callable`);
|
|
141
295
|
}
|
|
142
296
|
const callable = fnValue;
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
break;
|
|
166
|
-
case null:
|
|
167
|
-
jsonSchemaType = "string";
|
|
168
|
-
break;
|
|
297
|
+
let description;
|
|
298
|
+
if (callable.kind === "script") {
|
|
299
|
+
description = callable.annotations["description"] ?? "";
|
|
300
|
+
} else {
|
|
301
|
+
description = callable.description ?? "";
|
|
302
|
+
}
|
|
303
|
+
let inputSchema;
|
|
304
|
+
const params = callable.kind === "application" ? callable.params ?? [] : callable.kind === "script" ? callable.params : [];
|
|
305
|
+
if (params.length > 0) {
|
|
306
|
+
const properties = {};
|
|
307
|
+
const required = [];
|
|
308
|
+
for (const param of params) {
|
|
309
|
+
const property = {};
|
|
310
|
+
if (param.typeName !== null) {
|
|
311
|
+
const descriptor = {
|
|
312
|
+
[param.name]: { type: param.typeName }
|
|
313
|
+
};
|
|
314
|
+
const schema = buildJsonSchema(descriptor);
|
|
315
|
+
const built = schema.properties[param.name];
|
|
316
|
+
if (built !== void 0) {
|
|
317
|
+
Object.assign(property, built);
|
|
318
|
+
}
|
|
169
319
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
320
|
+
let paramDesc;
|
|
321
|
+
if (callable.kind === "script") {
|
|
322
|
+
const annot = callable.paramAnnotations[param.name];
|
|
323
|
+
paramDesc = annot?.["description"] ?? "";
|
|
324
|
+
} else {
|
|
325
|
+
paramDesc = param.description ?? "";
|
|
326
|
+
}
|
|
327
|
+
if (paramDesc) {
|
|
328
|
+
property["description"] = paramDesc;
|
|
175
329
|
}
|
|
176
330
|
properties[param.name] = property;
|
|
177
331
|
if (param.defaultValue === null) {
|
|
178
332
|
required.push(param.name);
|
|
179
333
|
}
|
|
180
334
|
}
|
|
335
|
+
inputSchema = {
|
|
336
|
+
type: "object",
|
|
337
|
+
properties,
|
|
338
|
+
required
|
|
339
|
+
};
|
|
340
|
+
} else {
|
|
341
|
+
inputSchema = { type: "object", properties: {}, required: [] };
|
|
181
342
|
}
|
|
182
343
|
return {
|
|
183
344
|
name,
|
|
184
345
|
description,
|
|
185
|
-
input_schema:
|
|
186
|
-
type: "object",
|
|
187
|
-
properties,
|
|
188
|
-
required
|
|
189
|
-
}
|
|
346
|
+
input_schema: inputSchema
|
|
190
347
|
};
|
|
191
348
|
});
|
|
192
349
|
const providerTools = callbacks.buildTools(toolDescriptors);
|
|
@@ -203,7 +360,7 @@ async function executeToolLoop(messages, tools, maxErrors, callbacks, emitEvent,
|
|
|
203
360
|
response = await callbacks.callAPI(currentMessages, providerTools);
|
|
204
361
|
} catch (error) {
|
|
205
362
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
206
|
-
throw new
|
|
363
|
+
throw new RuntimeError4("RILL-R004", `Provider API error: ${message}`, void 0, { cause: error });
|
|
207
364
|
}
|
|
208
365
|
if (typeof response === "object" && response !== null && "usage" in response) {
|
|
209
366
|
const usage = response["usage"];
|
|
@@ -215,7 +372,17 @@ async function executeToolLoop(messages, tools, maxErrors, callbacks, emitEvent,
|
|
|
215
372
|
totalOutputTokens += outputTokens;
|
|
216
373
|
}
|
|
217
374
|
}
|
|
218
|
-
const
|
|
375
|
+
const rawToolCalls = callbacks.extractToolCalls(response);
|
|
376
|
+
const nameMap = /* @__PURE__ */ new Map();
|
|
377
|
+
const toolCalls = rawToolCalls?.map((tc) => {
|
|
378
|
+
const sanitized = sanitizeToolName(tc.name);
|
|
379
|
+
if (sanitized !== tc.name)
|
|
380
|
+
nameMap.set(tc.name, sanitized);
|
|
381
|
+
return sanitized !== tc.name ? { ...tc, name: sanitized } : tc;
|
|
382
|
+
}) ?? null;
|
|
383
|
+
if (nameMap.size > 0) {
|
|
384
|
+
patchResponseToolCallNames(response, nameMap);
|
|
385
|
+
}
|
|
219
386
|
if (toolCalls === null || toolCalls.length === 0) {
|
|
220
387
|
return {
|
|
221
388
|
response,
|
|
@@ -240,7 +407,7 @@ async function executeToolLoop(messages, tools, maxErrors, callbacks, emitEvent,
|
|
|
240
407
|
const duration = Date.now() - toolStartTime;
|
|
241
408
|
consecutiveErrors++;
|
|
242
409
|
let originalError;
|
|
243
|
-
if (error instanceof
|
|
410
|
+
if (error instanceof RuntimeError4) {
|
|
244
411
|
const prefix = `Invalid tool input for ${name}: `;
|
|
245
412
|
if (error.message.startsWith(prefix)) {
|
|
246
413
|
originalError = error.message.slice(prefix.length);
|
|
@@ -265,12 +432,20 @@ async function executeToolLoop(messages, tools, maxErrors, callbacks, emitEvent,
|
|
|
265
432
|
duration
|
|
266
433
|
});
|
|
267
434
|
if (consecutiveErrors >= maxErrors) {
|
|
268
|
-
throw new
|
|
435
|
+
throw new RuntimeError4("RILL-R004", `Tool execution failed: ${maxErrors} consecutive errors (last: ${name}: ${originalError})`);
|
|
269
436
|
}
|
|
270
437
|
}
|
|
271
438
|
}
|
|
439
|
+
const assistantMessage = callbacks.formatAssistantMessage(response);
|
|
440
|
+
if (assistantMessage != null) {
|
|
441
|
+
currentMessages.push(assistantMessage);
|
|
442
|
+
}
|
|
272
443
|
const toolResultMessage = callbacks.formatToolResult(toolResults);
|
|
273
|
-
|
|
444
|
+
if (Array.isArray(toolResultMessage)) {
|
|
445
|
+
currentMessages.push(...toolResultMessage);
|
|
446
|
+
} else {
|
|
447
|
+
currentMessages.push(toolResultMessage);
|
|
448
|
+
}
|
|
274
449
|
}
|
|
275
450
|
return {
|
|
276
451
|
response: null,
|
|
@@ -298,6 +473,35 @@ var detectGeminiError = (error) => {
|
|
|
298
473
|
}
|
|
299
474
|
return null;
|
|
300
475
|
};
|
|
476
|
+
function toGeminiSchema(prop) {
|
|
477
|
+
let schemaType = Type.STRING;
|
|
478
|
+
if (prop.type === "number") schemaType = Type.NUMBER;
|
|
479
|
+
if (prop.type === "boolean") schemaType = Type.BOOLEAN;
|
|
480
|
+
if (prop.type === "integer") schemaType = Type.INTEGER;
|
|
481
|
+
if (prop.type === "array") schemaType = Type.ARRAY;
|
|
482
|
+
if (prop.type === "object") schemaType = Type.OBJECT;
|
|
483
|
+
const schema = { type: schemaType };
|
|
484
|
+
if (prop.description !== void 0) {
|
|
485
|
+
schema.description = prop.description;
|
|
486
|
+
}
|
|
487
|
+
if (prop.enum !== void 0) {
|
|
488
|
+
schema.enum = prop.enum;
|
|
489
|
+
}
|
|
490
|
+
if (prop.type === "array" && prop.items !== void 0) {
|
|
491
|
+
schema.items = toGeminiSchema(prop.items);
|
|
492
|
+
}
|
|
493
|
+
if (prop.type === "object" && prop.properties !== void 0) {
|
|
494
|
+
const nestedProperties = {};
|
|
495
|
+
for (const [key, nestedProp] of Object.entries(prop.properties)) {
|
|
496
|
+
nestedProperties[key] = toGeminiSchema(nestedProp);
|
|
497
|
+
}
|
|
498
|
+
schema.properties = nestedProperties;
|
|
499
|
+
if (prop.required !== void 0) {
|
|
500
|
+
schema.required = prop.required;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
return schema;
|
|
504
|
+
}
|
|
301
505
|
function createGeminiExtension(config) {
|
|
302
506
|
validateApiKey(config.api_key);
|
|
303
507
|
validateModel(config.model);
|
|
@@ -340,7 +544,7 @@ function createGeminiExtension(config) {
|
|
|
340
544
|
const text = args[0];
|
|
341
545
|
const options = args[1] ?? {};
|
|
342
546
|
if (text.trim().length === 0) {
|
|
343
|
-
throw new
|
|
547
|
+
throw new RuntimeError5("RILL-R004", "prompt text cannot be empty");
|
|
344
548
|
}
|
|
345
549
|
const system = typeof options["system"] === "string" ? options["system"] : factorySystem;
|
|
346
550
|
const maxTokens = typeof options["max_tokens"] === "number" ? options["max_tokens"] : factoryMaxTokens;
|
|
@@ -389,12 +593,14 @@ function createGeminiExtension(config) {
|
|
|
389
593
|
subsystem: "extension:gemini",
|
|
390
594
|
duration,
|
|
391
595
|
model: factoryModel,
|
|
392
|
-
usage: result2.usage
|
|
596
|
+
usage: result2.usage,
|
|
597
|
+
request: contents,
|
|
598
|
+
content
|
|
393
599
|
});
|
|
394
600
|
return result2;
|
|
395
601
|
} catch (error) {
|
|
396
602
|
const duration = Date.now() - startTime;
|
|
397
|
-
const rillError = error instanceof
|
|
603
|
+
const rillError = error instanceof RuntimeError5 ? error : mapProviderError("Gemini", error, detectGeminiError);
|
|
398
604
|
emitExtensionEvent(ctx, {
|
|
399
605
|
event: "gemini:error",
|
|
400
606
|
subsystem: "extension:gemini",
|
|
@@ -419,7 +625,7 @@ function createGeminiExtension(config) {
|
|
|
419
625
|
const messages = args[0];
|
|
420
626
|
const options = args[1] ?? {};
|
|
421
627
|
if (messages.length === 0) {
|
|
422
|
-
throw new
|
|
628
|
+
throw new RuntimeError5(
|
|
423
629
|
"RILL-R004",
|
|
424
630
|
"messages list cannot be empty"
|
|
425
631
|
);
|
|
@@ -430,18 +636,18 @@ function createGeminiExtension(config) {
|
|
|
430
636
|
for (let i = 0; i < messages.length; i++) {
|
|
431
637
|
const msg = messages[i];
|
|
432
638
|
if (!msg || typeof msg !== "object" || !("role" in msg)) {
|
|
433
|
-
throw new
|
|
639
|
+
throw new RuntimeError5(
|
|
434
640
|
"RILL-R004",
|
|
435
641
|
"message missing required 'role' field"
|
|
436
642
|
);
|
|
437
643
|
}
|
|
438
644
|
const role = msg["role"];
|
|
439
645
|
if (role !== "user" && role !== "assistant" && role !== "tool") {
|
|
440
|
-
throw new
|
|
646
|
+
throw new RuntimeError5("RILL-R004", `invalid role '${role}'`);
|
|
441
647
|
}
|
|
442
648
|
if (role === "user" || role === "tool") {
|
|
443
649
|
if (!("content" in msg) || typeof msg["content"] !== "string") {
|
|
444
|
-
throw new
|
|
650
|
+
throw new RuntimeError5(
|
|
445
651
|
"RILL-R004",
|
|
446
652
|
`${role} message requires 'content'`
|
|
447
653
|
);
|
|
@@ -454,7 +660,7 @@ function createGeminiExtension(config) {
|
|
|
454
660
|
const hasContent = "content" in msg && msg["content"];
|
|
455
661
|
const hasToolCalls = "tool_calls" in msg && msg["tool_calls"];
|
|
456
662
|
if (!hasContent && !hasToolCalls) {
|
|
457
|
-
throw new
|
|
663
|
+
throw new RuntimeError5(
|
|
458
664
|
"RILL-R004",
|
|
459
665
|
"assistant message requires 'content' or 'tool_calls'"
|
|
460
666
|
);
|
|
@@ -511,12 +717,14 @@ function createGeminiExtension(config) {
|
|
|
511
717
|
subsystem: "extension:gemini",
|
|
512
718
|
duration,
|
|
513
719
|
model: factoryModel,
|
|
514
|
-
usage: result2.usage
|
|
720
|
+
usage: result2.usage,
|
|
721
|
+
request: contents,
|
|
722
|
+
content
|
|
515
723
|
});
|
|
516
724
|
return result2;
|
|
517
725
|
} catch (error) {
|
|
518
726
|
const duration = Date.now() - startTime;
|
|
519
|
-
const rillError = error instanceof
|
|
727
|
+
const rillError = error instanceof RuntimeError5 ? error : mapProviderError("Gemini", error, detectGeminiError);
|
|
520
728
|
emitExtensionEvent(ctx, {
|
|
521
729
|
event: "gemini:error",
|
|
522
730
|
subsystem: "extension:gemini",
|
|
@@ -544,7 +752,7 @@ function createGeminiExtension(config) {
|
|
|
544
752
|
});
|
|
545
753
|
const embedding = response.embeddings?.[0];
|
|
546
754
|
if (!embedding || !embedding.values || embedding.values.length === 0) {
|
|
547
|
-
throw new
|
|
755
|
+
throw new RuntimeError5(
|
|
548
756
|
"RILL-R004",
|
|
549
757
|
"Gemini: empty embedding returned"
|
|
550
758
|
);
|
|
@@ -562,7 +770,7 @@ function createGeminiExtension(config) {
|
|
|
562
770
|
return vector;
|
|
563
771
|
} catch (error) {
|
|
564
772
|
const duration = Date.now() - startTime;
|
|
565
|
-
const rillError = error instanceof
|
|
773
|
+
const rillError = error instanceof RuntimeError5 ? error : mapProviderError("Gemini", error, detectGeminiError);
|
|
566
774
|
emitExtensionEvent(ctx, {
|
|
567
775
|
event: "gemini:error",
|
|
568
776
|
subsystem: "extension:gemini",
|
|
@@ -593,14 +801,14 @@ function createGeminiExtension(config) {
|
|
|
593
801
|
});
|
|
594
802
|
const vectors = [];
|
|
595
803
|
if (!response.embeddings || response.embeddings.length === 0) {
|
|
596
|
-
throw new
|
|
804
|
+
throw new RuntimeError5(
|
|
597
805
|
"RILL-R004",
|
|
598
806
|
"Gemini: empty embeddings returned"
|
|
599
807
|
);
|
|
600
808
|
}
|
|
601
809
|
for (const embedding of response.embeddings) {
|
|
602
810
|
if (!embedding || !embedding.values || embedding.values.length === 0) {
|
|
603
|
-
throw new
|
|
811
|
+
throw new RuntimeError5(
|
|
604
812
|
"RILL-R004",
|
|
605
813
|
"Gemini: empty embedding returned"
|
|
606
814
|
);
|
|
@@ -623,7 +831,7 @@ function createGeminiExtension(config) {
|
|
|
623
831
|
return vectors;
|
|
624
832
|
} catch (error) {
|
|
625
833
|
const duration = Date.now() - startTime;
|
|
626
|
-
const rillError = error instanceof
|
|
834
|
+
const rillError = error instanceof RuntimeError5 ? error : mapProviderError("Gemini", error, detectGeminiError);
|
|
627
835
|
emitExtensionEvent(ctx, {
|
|
628
836
|
event: "gemini:error",
|
|
629
837
|
subsystem: "extension:gemini",
|
|
@@ -648,39 +856,15 @@ function createGeminiExtension(config) {
|
|
|
648
856
|
const prompt = args[0];
|
|
649
857
|
const options = args[1] ?? {};
|
|
650
858
|
if (prompt.trim().length === 0) {
|
|
651
|
-
throw new
|
|
859
|
+
throw new RuntimeError5("RILL-R004", "prompt text cannot be empty");
|
|
652
860
|
}
|
|
653
|
-
if (!("tools" in options) || !
|
|
654
|
-
throw new
|
|
861
|
+
if (!("tools" in options) || !isDict2(options["tools"])) {
|
|
862
|
+
throw new RuntimeError5(
|
|
655
863
|
"RILL-R004",
|
|
656
864
|
"tool_loop requires 'tools' option"
|
|
657
865
|
);
|
|
658
866
|
}
|
|
659
|
-
const
|
|
660
|
-
const toolsDict = {};
|
|
661
|
-
for (const descriptor of toolDescriptors) {
|
|
662
|
-
const name = typeof descriptor["name"] === "string" ? descriptor["name"] : null;
|
|
663
|
-
if (!name) {
|
|
664
|
-
throw new RuntimeError4(
|
|
665
|
-
"RILL-R004",
|
|
666
|
-
"tool descriptor missing name"
|
|
667
|
-
);
|
|
668
|
-
}
|
|
669
|
-
const toolFnValue = descriptor["fn"];
|
|
670
|
-
if (!toolFnValue) {
|
|
671
|
-
throw new RuntimeError4(
|
|
672
|
-
"RILL-R004",
|
|
673
|
-
`tool '${name}' missing fn property`
|
|
674
|
-
);
|
|
675
|
-
}
|
|
676
|
-
if (!isCallable2(toolFnValue)) {
|
|
677
|
-
throw new RuntimeError4(
|
|
678
|
-
"RILL-R004",
|
|
679
|
-
`tool '${name}' fn must be callable`
|
|
680
|
-
);
|
|
681
|
-
}
|
|
682
|
-
toolsDict[name] = toolFnValue;
|
|
683
|
-
}
|
|
867
|
+
const toolsDict = options["tools"];
|
|
684
868
|
const system = typeof options["system"] === "string" ? options["system"] : factorySystem;
|
|
685
869
|
const maxTokens = typeof options["max_tokens"] === "number" ? options["max_tokens"] : factoryMaxTokens;
|
|
686
870
|
const maxTurns = typeof options["max_turns"] === "number" ? options["max_turns"] : 10;
|
|
@@ -775,6 +959,21 @@ function createGeminiExtension(config) {
|
|
|
775
959
|
};
|
|
776
960
|
});
|
|
777
961
|
},
|
|
962
|
+
// Extract the model's content from Gemini response for conversation history
|
|
963
|
+
formatAssistantMessage: (response2) => {
|
|
964
|
+
if (!response2 || typeof response2 !== "object" || !("candidates" in response2)) {
|
|
965
|
+
return null;
|
|
966
|
+
}
|
|
967
|
+
const candidates = response2.candidates;
|
|
968
|
+
if (!Array.isArray(candidates) || candidates.length === 0) {
|
|
969
|
+
return null;
|
|
970
|
+
}
|
|
971
|
+
const candidate = candidates[0];
|
|
972
|
+
if (!candidate || typeof candidate !== "object" || !("content" in candidate)) {
|
|
973
|
+
return null;
|
|
974
|
+
}
|
|
975
|
+
return candidate.content;
|
|
976
|
+
},
|
|
778
977
|
// Format tool results into Gemini message format
|
|
779
978
|
formatToolResult: (toolResults) => {
|
|
780
979
|
const functionResponseParts = toolResults.map((tr) => ({
|
|
@@ -807,7 +1006,8 @@ function createGeminiExtension(config) {
|
|
|
807
1006
|
...data
|
|
808
1007
|
});
|
|
809
1008
|
},
|
|
810
|
-
maxTurns
|
|
1009
|
+
maxTurns,
|
|
1010
|
+
ctx
|
|
811
1011
|
);
|
|
812
1012
|
const response = loopResult.response;
|
|
813
1013
|
const content = response && typeof response === "object" && "text" in response ? response.text ?? "" : "";
|
|
@@ -829,12 +1029,14 @@ function createGeminiExtension(config) {
|
|
|
829
1029
|
subsystem: "extension:gemini",
|
|
830
1030
|
turns: result2.turns,
|
|
831
1031
|
total_duration: duration,
|
|
832
|
-
usage: result2.usage
|
|
1032
|
+
usage: result2.usage,
|
|
1033
|
+
request: contents,
|
|
1034
|
+
content
|
|
833
1035
|
});
|
|
834
1036
|
return result2;
|
|
835
1037
|
} catch (error) {
|
|
836
1038
|
const duration = Date.now() - startTime;
|
|
837
|
-
const rillError = error instanceof
|
|
1039
|
+
const rillError = error instanceof RuntimeError5 ? error : mapProviderError("Gemini", error, detectGeminiError);
|
|
838
1040
|
emitExtensionEvent(ctx, {
|
|
839
1041
|
event: "gemini:error",
|
|
840
1042
|
subsystem: "extension:gemini",
|
|
@@ -846,6 +1048,130 @@ function createGeminiExtension(config) {
|
|
|
846
1048
|
},
|
|
847
1049
|
description: "Execute tool-use loop with Gemini API",
|
|
848
1050
|
returnType: "dict"
|
|
1051
|
+
},
|
|
1052
|
+
// IR-3: gemini::generate
|
|
1053
|
+
generate: {
|
|
1054
|
+
params: [
|
|
1055
|
+
{ name: "prompt", type: "string" },
|
|
1056
|
+
{ name: "options", type: "dict" }
|
|
1057
|
+
],
|
|
1058
|
+
fn: async (args, ctx) => {
|
|
1059
|
+
const startTime = Date.now();
|
|
1060
|
+
try {
|
|
1061
|
+
const prompt = args[0];
|
|
1062
|
+
const options = args[1] ?? {};
|
|
1063
|
+
if (!("schema" in options) || options["schema"] === null || options["schema"] === void 0) {
|
|
1064
|
+
throw new RuntimeError5(
|
|
1065
|
+
"RILL-R004",
|
|
1066
|
+
"generate requires 'schema' option"
|
|
1067
|
+
);
|
|
1068
|
+
}
|
|
1069
|
+
const rillSchema = options["schema"];
|
|
1070
|
+
const jsonSchema = buildJsonSchema(rillSchema);
|
|
1071
|
+
const geminiProperties = {};
|
|
1072
|
+
for (const [key, prop] of Object.entries(jsonSchema.properties)) {
|
|
1073
|
+
geminiProperties[key] = toGeminiSchema(prop);
|
|
1074
|
+
}
|
|
1075
|
+
const responseSchema = {
|
|
1076
|
+
type: Type.OBJECT,
|
|
1077
|
+
properties: geminiProperties,
|
|
1078
|
+
required: jsonSchema.required
|
|
1079
|
+
};
|
|
1080
|
+
const system = typeof options["system"] === "string" ? options["system"] : factorySystem;
|
|
1081
|
+
const maxTokens = typeof options["max_tokens"] === "number" ? options["max_tokens"] : factoryMaxTokens;
|
|
1082
|
+
const contents = [];
|
|
1083
|
+
if ("messages" in options && Array.isArray(options["messages"])) {
|
|
1084
|
+
const prependedMessages = options["messages"];
|
|
1085
|
+
for (const msg of prependedMessages) {
|
|
1086
|
+
if (typeof msg === "object" && msg !== null && "role" in msg && "content" in msg) {
|
|
1087
|
+
const role = msg["role"];
|
|
1088
|
+
if (role === "user") {
|
|
1089
|
+
contents.push({
|
|
1090
|
+
role: "user",
|
|
1091
|
+
parts: [{ text: msg["content"] }]
|
|
1092
|
+
});
|
|
1093
|
+
} else if (role === "assistant") {
|
|
1094
|
+
contents.push({
|
|
1095
|
+
role: "model",
|
|
1096
|
+
parts: [{ text: msg["content"] }]
|
|
1097
|
+
});
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
contents.push({
|
|
1103
|
+
role: "user",
|
|
1104
|
+
parts: [{ text: prompt }]
|
|
1105
|
+
});
|
|
1106
|
+
const apiConfig = {
|
|
1107
|
+
responseSchema,
|
|
1108
|
+
responseMimeType: "application/json"
|
|
1109
|
+
};
|
|
1110
|
+
if (system !== void 0) {
|
|
1111
|
+
apiConfig.systemInstruction = system;
|
|
1112
|
+
}
|
|
1113
|
+
if (maxTokens !== void 0) {
|
|
1114
|
+
apiConfig.maxOutputTokens = maxTokens;
|
|
1115
|
+
}
|
|
1116
|
+
if (factoryTemperature !== void 0) {
|
|
1117
|
+
apiConfig.temperature = factoryTemperature;
|
|
1118
|
+
}
|
|
1119
|
+
const response = await client.models.generateContent({
|
|
1120
|
+
model: factoryModel,
|
|
1121
|
+
contents,
|
|
1122
|
+
config: apiConfig
|
|
1123
|
+
});
|
|
1124
|
+
const raw = response.text ?? "";
|
|
1125
|
+
let data;
|
|
1126
|
+
try {
|
|
1127
|
+
data = JSON.parse(raw);
|
|
1128
|
+
} catch (parseError) {
|
|
1129
|
+
const detail = parseError instanceof Error ? parseError.message : String(parseError);
|
|
1130
|
+
throw new RuntimeError5(
|
|
1131
|
+
"RILL-R004",
|
|
1132
|
+
`generate: failed to parse response JSON: ${detail}`
|
|
1133
|
+
);
|
|
1134
|
+
}
|
|
1135
|
+
const inputTokens = response.usageMetadata?.promptTokenCount ?? 0;
|
|
1136
|
+
const outputTokens = response.usageMetadata?.candidatesTokenCount ?? 0;
|
|
1137
|
+
const stopReason = response.candidates?.[0]?.finishReason ?? "stop";
|
|
1138
|
+
const id = response.responseId ?? "";
|
|
1139
|
+
const generateResult = {
|
|
1140
|
+
data,
|
|
1141
|
+
raw,
|
|
1142
|
+
model: factoryModel,
|
|
1143
|
+
usage: {
|
|
1144
|
+
input: inputTokens,
|
|
1145
|
+
output: outputTokens
|
|
1146
|
+
},
|
|
1147
|
+
stop_reason: stopReason,
|
|
1148
|
+
id
|
|
1149
|
+
};
|
|
1150
|
+
const duration = Date.now() - startTime;
|
|
1151
|
+
emitExtensionEvent(ctx, {
|
|
1152
|
+
event: "gemini:generate",
|
|
1153
|
+
subsystem: "extension:gemini",
|
|
1154
|
+
duration,
|
|
1155
|
+
model: factoryModel,
|
|
1156
|
+
usage: generateResult.usage,
|
|
1157
|
+
request: contents,
|
|
1158
|
+
content: raw
|
|
1159
|
+
});
|
|
1160
|
+
return generateResult;
|
|
1161
|
+
} catch (error) {
|
|
1162
|
+
const duration = Date.now() - startTime;
|
|
1163
|
+
const rillError = error instanceof RuntimeError5 ? error : mapProviderError("Gemini", error, detectGeminiError);
|
|
1164
|
+
emitExtensionEvent(ctx, {
|
|
1165
|
+
event: "gemini:error",
|
|
1166
|
+
subsystem: "extension:gemini",
|
|
1167
|
+
error: rillError.message,
|
|
1168
|
+
duration
|
|
1169
|
+
});
|
|
1170
|
+
throw rillError;
|
|
1171
|
+
}
|
|
1172
|
+
},
|
|
1173
|
+
description: "Generate structured output from Gemini API",
|
|
1174
|
+
returnType: "dict"
|
|
849
1175
|
}
|
|
850
1176
|
};
|
|
851
1177
|
result.dispose = dispose;
|
|
@@ -854,7 +1180,19 @@ function createGeminiExtension(config) {
|
|
|
854
1180
|
|
|
855
1181
|
// src/index.ts
|
|
856
1182
|
var VERSION = "0.0.1";
|
|
1183
|
+
var configSchema = {
|
|
1184
|
+
api_key: { type: "string", required: true, secret: true },
|
|
1185
|
+
model: { type: "string", required: true },
|
|
1186
|
+
base_url: { type: "string" },
|
|
1187
|
+
temperature: { type: "number" },
|
|
1188
|
+
max_tokens: { type: "number" },
|
|
1189
|
+
timeout: { type: "number" },
|
|
1190
|
+
max_retries: { type: "number" },
|
|
1191
|
+
system: { type: "string" },
|
|
1192
|
+
embed_model: { type: "string" }
|
|
1193
|
+
};
|
|
857
1194
|
export {
|
|
858
1195
|
VERSION,
|
|
1196
|
+
configSchema,
|
|
859
1197
|
createGeminiExtension
|
|
860
1198
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rcrsr/rill-ext-gemini",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "rill extension for Google Gemini API integration",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Andre Bremer",
|
|
@@ -17,14 +17,14 @@
|
|
|
17
17
|
"scripting"
|
|
18
18
|
],
|
|
19
19
|
"peerDependencies": {
|
|
20
|
-
"@rcrsr/rill": "^0.
|
|
20
|
+
"@rcrsr/rill": "^0.9.0"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
|
-
"@
|
|
23
|
+
"@rcrsr/rill": "^0.9.0",
|
|
24
|
+
"@types/node": "^25.3.0",
|
|
24
25
|
"dts-bundle-generator": "^9.5.1",
|
|
25
|
-
"tsup": "^8.5.
|
|
26
|
-
"undici-types": "^7.
|
|
27
|
-
"@rcrsr/rill": "^0.8.5",
|
|
26
|
+
"tsup": "^8.5.1",
|
|
27
|
+
"undici-types": "^7.22.0",
|
|
28
28
|
"@rcrsr/rill-ext-llm-shared": "^0.0.1"
|
|
29
29
|
},
|
|
30
30
|
"files": [
|
|
@@ -32,18 +32,18 @@
|
|
|
32
32
|
],
|
|
33
33
|
"repository": {
|
|
34
34
|
"type": "git",
|
|
35
|
-
"url": "git+https://github.com/rcrsr/rill.git",
|
|
36
|
-
"directory": "packages/ext/gemini"
|
|
35
|
+
"url": "git+https://github.com/rcrsr/rill-ext.git",
|
|
36
|
+
"directory": "packages/ext/llm-gemini"
|
|
37
37
|
},
|
|
38
|
-
"homepage": "https://
|
|
38
|
+
"homepage": "https://github.com/rcrsr/rill-ext/tree/main/packages/ext/llm-gemini#readme",
|
|
39
39
|
"bugs": {
|
|
40
|
-
"url": "https://github.com/rcrsr/rill/issues"
|
|
40
|
+
"url": "https://github.com/rcrsr/rill-ext/issues"
|
|
41
41
|
},
|
|
42
42
|
"publishConfig": {
|
|
43
43
|
"access": "public"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@google/genai": "^1.
|
|
46
|
+
"@google/genai": "^1.42.0"
|
|
47
47
|
},
|
|
48
48
|
"scripts": {
|
|
49
49
|
"build": "tsup && dts-bundle-generator --config dts-bundle-generator.config.cjs",
|