@rcrsr/rill-ext-gemini 0.8.6 → 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 -105
- package/package.json +10 -10
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,77 +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;
|
|
169
|
-
default:
|
|
170
|
-
jsonSchemaType = "string";
|
|
171
|
-
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
|
+
}
|
|
172
319
|
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
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;
|
|
178
329
|
}
|
|
179
330
|
properties[param.name] = property;
|
|
180
331
|
if (param.defaultValue === null) {
|
|
181
332
|
required.push(param.name);
|
|
182
333
|
}
|
|
183
334
|
}
|
|
335
|
+
inputSchema = {
|
|
336
|
+
type: "object",
|
|
337
|
+
properties,
|
|
338
|
+
required
|
|
339
|
+
};
|
|
340
|
+
} else {
|
|
341
|
+
inputSchema = { type: "object", properties: {}, required: [] };
|
|
184
342
|
}
|
|
185
343
|
return {
|
|
186
344
|
name,
|
|
187
345
|
description,
|
|
188
|
-
input_schema:
|
|
189
|
-
type: "object",
|
|
190
|
-
properties,
|
|
191
|
-
required
|
|
192
|
-
}
|
|
346
|
+
input_schema: inputSchema
|
|
193
347
|
};
|
|
194
348
|
});
|
|
195
349
|
const providerTools = callbacks.buildTools(toolDescriptors);
|
|
@@ -206,7 +360,7 @@ async function executeToolLoop(messages, tools, maxErrors, callbacks, emitEvent,
|
|
|
206
360
|
response = await callbacks.callAPI(currentMessages, providerTools);
|
|
207
361
|
} catch (error) {
|
|
208
362
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
209
|
-
throw new
|
|
363
|
+
throw new RuntimeError4("RILL-R004", `Provider API error: ${message}`, void 0, { cause: error });
|
|
210
364
|
}
|
|
211
365
|
if (typeof response === "object" && response !== null && "usage" in response) {
|
|
212
366
|
const usage = response["usage"];
|
|
@@ -218,7 +372,17 @@ async function executeToolLoop(messages, tools, maxErrors, callbacks, emitEvent,
|
|
|
218
372
|
totalOutputTokens += outputTokens;
|
|
219
373
|
}
|
|
220
374
|
}
|
|
221
|
-
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
|
+
}
|
|
222
386
|
if (toolCalls === null || toolCalls.length === 0) {
|
|
223
387
|
return {
|
|
224
388
|
response,
|
|
@@ -243,7 +407,7 @@ async function executeToolLoop(messages, tools, maxErrors, callbacks, emitEvent,
|
|
|
243
407
|
const duration = Date.now() - toolStartTime;
|
|
244
408
|
consecutiveErrors++;
|
|
245
409
|
let originalError;
|
|
246
|
-
if (error instanceof
|
|
410
|
+
if (error instanceof RuntimeError4) {
|
|
247
411
|
const prefix = `Invalid tool input for ${name}: `;
|
|
248
412
|
if (error.message.startsWith(prefix)) {
|
|
249
413
|
originalError = error.message.slice(prefix.length);
|
|
@@ -268,12 +432,20 @@ async function executeToolLoop(messages, tools, maxErrors, callbacks, emitEvent,
|
|
|
268
432
|
duration
|
|
269
433
|
});
|
|
270
434
|
if (consecutiveErrors >= maxErrors) {
|
|
271
|
-
throw new
|
|
435
|
+
throw new RuntimeError4("RILL-R004", `Tool execution failed: ${maxErrors} consecutive errors (last: ${name}: ${originalError})`);
|
|
272
436
|
}
|
|
273
437
|
}
|
|
274
438
|
}
|
|
439
|
+
const assistantMessage = callbacks.formatAssistantMessage(response);
|
|
440
|
+
if (assistantMessage != null) {
|
|
441
|
+
currentMessages.push(assistantMessage);
|
|
442
|
+
}
|
|
275
443
|
const toolResultMessage = callbacks.formatToolResult(toolResults);
|
|
276
|
-
|
|
444
|
+
if (Array.isArray(toolResultMessage)) {
|
|
445
|
+
currentMessages.push(...toolResultMessage);
|
|
446
|
+
} else {
|
|
447
|
+
currentMessages.push(toolResultMessage);
|
|
448
|
+
}
|
|
277
449
|
}
|
|
278
450
|
return {
|
|
279
451
|
response: null,
|
|
@@ -301,6 +473,35 @@ var detectGeminiError = (error) => {
|
|
|
301
473
|
}
|
|
302
474
|
return null;
|
|
303
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
|
+
}
|
|
304
505
|
function createGeminiExtension(config) {
|
|
305
506
|
validateApiKey(config.api_key);
|
|
306
507
|
validateModel(config.model);
|
|
@@ -343,7 +544,7 @@ function createGeminiExtension(config) {
|
|
|
343
544
|
const text = args[0];
|
|
344
545
|
const options = args[1] ?? {};
|
|
345
546
|
if (text.trim().length === 0) {
|
|
346
|
-
throw new
|
|
547
|
+
throw new RuntimeError5("RILL-R004", "prompt text cannot be empty");
|
|
347
548
|
}
|
|
348
549
|
const system = typeof options["system"] === "string" ? options["system"] : factorySystem;
|
|
349
550
|
const maxTokens = typeof options["max_tokens"] === "number" ? options["max_tokens"] : factoryMaxTokens;
|
|
@@ -392,12 +593,14 @@ function createGeminiExtension(config) {
|
|
|
392
593
|
subsystem: "extension:gemini",
|
|
393
594
|
duration,
|
|
394
595
|
model: factoryModel,
|
|
395
|
-
usage: result2.usage
|
|
596
|
+
usage: result2.usage,
|
|
597
|
+
request: contents,
|
|
598
|
+
content
|
|
396
599
|
});
|
|
397
600
|
return result2;
|
|
398
601
|
} catch (error) {
|
|
399
602
|
const duration = Date.now() - startTime;
|
|
400
|
-
const rillError = error instanceof
|
|
603
|
+
const rillError = error instanceof RuntimeError5 ? error : mapProviderError("Gemini", error, detectGeminiError);
|
|
401
604
|
emitExtensionEvent(ctx, {
|
|
402
605
|
event: "gemini:error",
|
|
403
606
|
subsystem: "extension:gemini",
|
|
@@ -422,7 +625,7 @@ function createGeminiExtension(config) {
|
|
|
422
625
|
const messages = args[0];
|
|
423
626
|
const options = args[1] ?? {};
|
|
424
627
|
if (messages.length === 0) {
|
|
425
|
-
throw new
|
|
628
|
+
throw new RuntimeError5(
|
|
426
629
|
"RILL-R004",
|
|
427
630
|
"messages list cannot be empty"
|
|
428
631
|
);
|
|
@@ -433,18 +636,18 @@ function createGeminiExtension(config) {
|
|
|
433
636
|
for (let i = 0; i < messages.length; i++) {
|
|
434
637
|
const msg = messages[i];
|
|
435
638
|
if (!msg || typeof msg !== "object" || !("role" in msg)) {
|
|
436
|
-
throw new
|
|
639
|
+
throw new RuntimeError5(
|
|
437
640
|
"RILL-R004",
|
|
438
641
|
"message missing required 'role' field"
|
|
439
642
|
);
|
|
440
643
|
}
|
|
441
644
|
const role = msg["role"];
|
|
442
645
|
if (role !== "user" && role !== "assistant" && role !== "tool") {
|
|
443
|
-
throw new
|
|
646
|
+
throw new RuntimeError5("RILL-R004", `invalid role '${role}'`);
|
|
444
647
|
}
|
|
445
648
|
if (role === "user" || role === "tool") {
|
|
446
649
|
if (!("content" in msg) || typeof msg["content"] !== "string") {
|
|
447
|
-
throw new
|
|
650
|
+
throw new RuntimeError5(
|
|
448
651
|
"RILL-R004",
|
|
449
652
|
`${role} message requires 'content'`
|
|
450
653
|
);
|
|
@@ -457,7 +660,7 @@ function createGeminiExtension(config) {
|
|
|
457
660
|
const hasContent = "content" in msg && msg["content"];
|
|
458
661
|
const hasToolCalls = "tool_calls" in msg && msg["tool_calls"];
|
|
459
662
|
if (!hasContent && !hasToolCalls) {
|
|
460
|
-
throw new
|
|
663
|
+
throw new RuntimeError5(
|
|
461
664
|
"RILL-R004",
|
|
462
665
|
"assistant message requires 'content' or 'tool_calls'"
|
|
463
666
|
);
|
|
@@ -514,12 +717,14 @@ function createGeminiExtension(config) {
|
|
|
514
717
|
subsystem: "extension:gemini",
|
|
515
718
|
duration,
|
|
516
719
|
model: factoryModel,
|
|
517
|
-
usage: result2.usage
|
|
720
|
+
usage: result2.usage,
|
|
721
|
+
request: contents,
|
|
722
|
+
content
|
|
518
723
|
});
|
|
519
724
|
return result2;
|
|
520
725
|
} catch (error) {
|
|
521
726
|
const duration = Date.now() - startTime;
|
|
522
|
-
const rillError = error instanceof
|
|
727
|
+
const rillError = error instanceof RuntimeError5 ? error : mapProviderError("Gemini", error, detectGeminiError);
|
|
523
728
|
emitExtensionEvent(ctx, {
|
|
524
729
|
event: "gemini:error",
|
|
525
730
|
subsystem: "extension:gemini",
|
|
@@ -547,7 +752,7 @@ function createGeminiExtension(config) {
|
|
|
547
752
|
});
|
|
548
753
|
const embedding = response.embeddings?.[0];
|
|
549
754
|
if (!embedding || !embedding.values || embedding.values.length === 0) {
|
|
550
|
-
throw new
|
|
755
|
+
throw new RuntimeError5(
|
|
551
756
|
"RILL-R004",
|
|
552
757
|
"Gemini: empty embedding returned"
|
|
553
758
|
);
|
|
@@ -565,7 +770,7 @@ function createGeminiExtension(config) {
|
|
|
565
770
|
return vector;
|
|
566
771
|
} catch (error) {
|
|
567
772
|
const duration = Date.now() - startTime;
|
|
568
|
-
const rillError = error instanceof
|
|
773
|
+
const rillError = error instanceof RuntimeError5 ? error : mapProviderError("Gemini", error, detectGeminiError);
|
|
569
774
|
emitExtensionEvent(ctx, {
|
|
570
775
|
event: "gemini:error",
|
|
571
776
|
subsystem: "extension:gemini",
|
|
@@ -596,14 +801,14 @@ function createGeminiExtension(config) {
|
|
|
596
801
|
});
|
|
597
802
|
const vectors = [];
|
|
598
803
|
if (!response.embeddings || response.embeddings.length === 0) {
|
|
599
|
-
throw new
|
|
804
|
+
throw new RuntimeError5(
|
|
600
805
|
"RILL-R004",
|
|
601
806
|
"Gemini: empty embeddings returned"
|
|
602
807
|
);
|
|
603
808
|
}
|
|
604
809
|
for (const embedding of response.embeddings) {
|
|
605
810
|
if (!embedding || !embedding.values || embedding.values.length === 0) {
|
|
606
|
-
throw new
|
|
811
|
+
throw new RuntimeError5(
|
|
607
812
|
"RILL-R004",
|
|
608
813
|
"Gemini: empty embedding returned"
|
|
609
814
|
);
|
|
@@ -626,7 +831,7 @@ function createGeminiExtension(config) {
|
|
|
626
831
|
return vectors;
|
|
627
832
|
} catch (error) {
|
|
628
833
|
const duration = Date.now() - startTime;
|
|
629
|
-
const rillError = error instanceof
|
|
834
|
+
const rillError = error instanceof RuntimeError5 ? error : mapProviderError("Gemini", error, detectGeminiError);
|
|
630
835
|
emitExtensionEvent(ctx, {
|
|
631
836
|
event: "gemini:error",
|
|
632
837
|
subsystem: "extension:gemini",
|
|
@@ -651,39 +856,15 @@ function createGeminiExtension(config) {
|
|
|
651
856
|
const prompt = args[0];
|
|
652
857
|
const options = args[1] ?? {};
|
|
653
858
|
if (prompt.trim().length === 0) {
|
|
654
|
-
throw new
|
|
859
|
+
throw new RuntimeError5("RILL-R004", "prompt text cannot be empty");
|
|
655
860
|
}
|
|
656
|
-
if (!("tools" in options) || !
|
|
657
|
-
throw new
|
|
861
|
+
if (!("tools" in options) || !isDict2(options["tools"])) {
|
|
862
|
+
throw new RuntimeError5(
|
|
658
863
|
"RILL-R004",
|
|
659
864
|
"tool_loop requires 'tools' option"
|
|
660
865
|
);
|
|
661
866
|
}
|
|
662
|
-
const
|
|
663
|
-
const toolsDict = {};
|
|
664
|
-
for (const descriptor of toolDescriptors) {
|
|
665
|
-
const name = typeof descriptor["name"] === "string" ? descriptor["name"] : null;
|
|
666
|
-
if (!name) {
|
|
667
|
-
throw new RuntimeError4(
|
|
668
|
-
"RILL-R004",
|
|
669
|
-
"tool descriptor missing name"
|
|
670
|
-
);
|
|
671
|
-
}
|
|
672
|
-
const toolFnValue = descriptor["fn"];
|
|
673
|
-
if (!toolFnValue) {
|
|
674
|
-
throw new RuntimeError4(
|
|
675
|
-
"RILL-R004",
|
|
676
|
-
`tool '${name}' missing fn property`
|
|
677
|
-
);
|
|
678
|
-
}
|
|
679
|
-
if (!isCallable2(toolFnValue)) {
|
|
680
|
-
throw new RuntimeError4(
|
|
681
|
-
"RILL-R004",
|
|
682
|
-
`tool '${name}' fn must be callable`
|
|
683
|
-
);
|
|
684
|
-
}
|
|
685
|
-
toolsDict[name] = toolFnValue;
|
|
686
|
-
}
|
|
867
|
+
const toolsDict = options["tools"];
|
|
687
868
|
const system = typeof options["system"] === "string" ? options["system"] : factorySystem;
|
|
688
869
|
const maxTokens = typeof options["max_tokens"] === "number" ? options["max_tokens"] : factoryMaxTokens;
|
|
689
870
|
const maxTurns = typeof options["max_turns"] === "number" ? options["max_turns"] : 10;
|
|
@@ -778,6 +959,21 @@ function createGeminiExtension(config) {
|
|
|
778
959
|
};
|
|
779
960
|
});
|
|
780
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
|
+
},
|
|
781
977
|
// Format tool results into Gemini message format
|
|
782
978
|
formatToolResult: (toolResults) => {
|
|
783
979
|
const functionResponseParts = toolResults.map((tr) => ({
|
|
@@ -810,7 +1006,8 @@ function createGeminiExtension(config) {
|
|
|
810
1006
|
...data
|
|
811
1007
|
});
|
|
812
1008
|
},
|
|
813
|
-
maxTurns
|
|
1009
|
+
maxTurns,
|
|
1010
|
+
ctx
|
|
814
1011
|
);
|
|
815
1012
|
const response = loopResult.response;
|
|
816
1013
|
const content = response && typeof response === "object" && "text" in response ? response.text ?? "" : "";
|
|
@@ -832,12 +1029,14 @@ function createGeminiExtension(config) {
|
|
|
832
1029
|
subsystem: "extension:gemini",
|
|
833
1030
|
turns: result2.turns,
|
|
834
1031
|
total_duration: duration,
|
|
835
|
-
usage: result2.usage
|
|
1032
|
+
usage: result2.usage,
|
|
1033
|
+
request: contents,
|
|
1034
|
+
content
|
|
836
1035
|
});
|
|
837
1036
|
return result2;
|
|
838
1037
|
} catch (error) {
|
|
839
1038
|
const duration = Date.now() - startTime;
|
|
840
|
-
const rillError = error instanceof
|
|
1039
|
+
const rillError = error instanceof RuntimeError5 ? error : mapProviderError("Gemini", error, detectGeminiError);
|
|
841
1040
|
emitExtensionEvent(ctx, {
|
|
842
1041
|
event: "gemini:error",
|
|
843
1042
|
subsystem: "extension:gemini",
|
|
@@ -849,6 +1048,130 @@ function createGeminiExtension(config) {
|
|
|
849
1048
|
},
|
|
850
1049
|
description: "Execute tool-use loop with Gemini API",
|
|
851
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"
|
|
852
1175
|
}
|
|
853
1176
|
};
|
|
854
1177
|
result.dispose = dispose;
|
|
@@ -857,7 +1180,19 @@ function createGeminiExtension(config) {
|
|
|
857
1180
|
|
|
858
1181
|
// src/index.ts
|
|
859
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
|
+
};
|
|
860
1194
|
export {
|
|
861
1195
|
VERSION,
|
|
1196
|
+
configSchema,
|
|
862
1197
|
createGeminiExtension
|
|
863
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.6",
|
|
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",
|
|
35
|
+
"url": "git+https://github.com/rcrsr/rill-ext.git",
|
|
36
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",
|