Skip to content

Commit bb0b304

Browse files
authored
[FEATURE] Google Gemini - Add support for structure JSON output (#14896)
1 parent f9644ce commit bb0b304

File tree

8 files changed

+226
-36
lines changed

8 files changed

+226
-36
lines changed
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import app from "../../google_gemini.app.mjs";
2+
import constants from "../../common/constants.mjs";
3+
4+
export default {
5+
props: {
6+
app,
7+
model: {
8+
propDefinition: [
9+
app,
10+
"model",
11+
() => ({
12+
filter: ({
13+
description,
14+
supportedGenerationMethods,
15+
}) => ![
16+
"discontinued",
17+
"deprecated",
18+
].some((keyword) => description.includes(keyword))
19+
&& supportedGenerationMethods?.includes(constants.MODEL_METHODS.GENERATE_CONTENT),
20+
}),
21+
],
22+
},
23+
},
24+
async additionalProps() {
25+
const {
26+
model,
27+
responseFormat,
28+
} = this;
29+
30+
const {
31+
outputTokenLimit,
32+
temperature,
33+
topP,
34+
topK,
35+
maxTemperature,
36+
} = await this.app.getModel({
37+
model,
38+
});
39+
40+
return {
41+
...(responseFormat && {
42+
responseSchema: {
43+
type: "string",
44+
label: "Response Schema",
45+
description: "Define the structure of the JSON response. Must be a valid JSON schema object. Leave empty to let Gemini determine the structure.",
46+
optional: true,
47+
},
48+
}),
49+
...(outputTokenLimit && {
50+
maxOutputTokens: {
51+
type: "integer",
52+
label: "Max Output Tokens",
53+
description: `The maximum number of tokens to generate in the response. Eg. \`${outputTokenLimit}\`.`,
54+
optional: true,
55+
max: outputTokenLimit,
56+
},
57+
}),
58+
...(temperature && {
59+
temperature: {
60+
type: "string",
61+
label: "Temperature",
62+
description: `Controls the randomness of the generated text. Lower values make the text more deterministic, while higher values make it more random. Eg. \`${temperature}\`.${maxTemperature
63+
? ` Where max temperature is \`${maxTemperature}\`.`
64+
: ""}`,
65+
optional: true,
66+
},
67+
}),
68+
...(topP && {
69+
topP: {
70+
type: "string",
71+
label: "Top P",
72+
description: `Controls the diversity of the generated text. Lower values make the text more deterministic, while higher values make it more random. Eg. \`${topP}\`.`,
73+
optional: true,
74+
},
75+
}),
76+
...(topK && {
77+
topK: {
78+
type: "integer",
79+
label: "Top K",
80+
description: `Controls the diversity of the generated text. Lower values make the text more deterministic, while higher values make it more random. Eg. \`${topK}\`.`,
81+
optional: true,
82+
},
83+
}),
84+
stopSequences: {
85+
type: "string[]",
86+
label: "Stop Sequences",
87+
description: "The set of character sequences (up to 5) that will stop output generation. If specified, the API will stop at the first appearance of a `stop_sequence`. The stop sequence will not be included as part of the response.",
88+
optional: true,
89+
},
90+
};
91+
},
92+
};

components/google_gemini/actions/generate-content-from-text-and-image/generate-content-from-text-and-image.mjs

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,41 @@
11
import fs from "fs";
22
import { ConfigurationError } from "@pipedream/platform";
3-
import app from "../../google_gemini.app.mjs";
3+
import common from "../common/generate-content.mjs";
4+
import utils from "../../common/utils.mjs";
45

56
export default {
7+
...common,
68
key: "google_gemini-generate-content-from-text-and-image",
79
name: "Generate Content from Text and Image",
810
description: "Generates content from both text and image input using the Gemini API. [See the documentation](https://ai.google.dev/tutorials/rest_quickstart#text-and-image_input)",
9-
version: "0.1.0",
11+
version: "0.1.1",
1012
type: "action",
1113
props: {
12-
app,
13-
model: {
14-
propDefinition: [
15-
app,
16-
"model",
17-
],
18-
},
14+
...common.props,
1915
text: {
2016
propDefinition: [
21-
app,
17+
common.props.app,
2218
"text",
2319
],
2420
},
2521
mimeType: {
2622
propDefinition: [
27-
app,
23+
common.props.app,
2824
"mimeType",
2925
],
3026
},
3127
imagePaths: {
3228
propDefinition: [
33-
app,
29+
common.props.app,
3430
"imagePaths",
3531
],
3632
},
33+
responseFormat: {
34+
propDefinition: [
35+
common.props.app,
36+
"responseFormat",
37+
],
38+
},
3739
},
3840
methods: {
3941
fileToGenerativePart(path, mimeType) {
@@ -55,6 +57,13 @@ export default {
5557
text,
5658
imagePaths,
5759
mimeType,
60+
responseFormat,
61+
responseSchema,
62+
maxOutputTokens,
63+
temperature,
64+
topP,
65+
topK,
66+
stopSequences,
5867
} = this;
5968

6069
if (!Array.isArray(imagePaths)) {
@@ -79,6 +88,21 @@ export default {
7988
],
8089
},
8190
],
91+
...(
92+
responseFormat || maxOutputTokens || temperature || topP || topK || stopSequences?.length
93+
? {
94+
generationConfig: {
95+
responseMimeType: "application/json",
96+
responseSchema: utils.parse(responseSchema),
97+
maxOutputTokens,
98+
temperature,
99+
topP,
100+
topK,
101+
stopSequences,
102+
},
103+
}
104+
: {}
105+
),
82106
},
83107
});
84108

components/google_gemini/actions/generate-content-from-text/generate-content-from-text.mjs

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,25 @@
1-
import app from "../../google_gemini.app.mjs";
1+
import common from "../common/generate-content.mjs";
2+
import utils from "../../common/utils.mjs";
23

34
export default {
5+
...common,
46
key: "google_gemini-generate-content-from-text",
57
name: "Generate Content from Text",
68
description: "Generates content from text input using the Google Gemini API. [See the documentation](https://ai.google.dev/tutorials/rest_quickstart#text-only_input)",
7-
version: "0.1.0",
9+
version: "0.1.1",
810
type: "action",
911
props: {
10-
app,
11-
model: {
12+
...common.props,
13+
text: {
1214
propDefinition: [
13-
app,
14-
"model",
15+
common.props.app,
16+
"text",
1517
],
1618
},
17-
text: {
19+
responseFormat: {
1820
propDefinition: [
19-
app,
20-
"text",
21+
common.props.app,
22+
"responseFormat",
2123
],
2224
},
2325
},
@@ -26,6 +28,13 @@ export default {
2628
app,
2729
model,
2830
text,
31+
responseFormat,
32+
responseSchema,
33+
maxOutputTokens,
34+
temperature,
35+
topP,
36+
topK,
37+
stopSequences,
2938
} = this;
3039

3140
const response = await app.generateContent({
@@ -41,6 +50,21 @@ export default {
4150
],
4251
},
4352
],
53+
...(
54+
responseFormat || maxOutputTokens || temperature || topP || topK || stopSequences?.length
55+
? {
56+
generationConfig: {
57+
responseMimeType: "application/json",
58+
responseSchema: utils.parse(responseSchema),
59+
maxOutputTokens,
60+
temperature,
61+
topP,
62+
topK,
63+
stopSequences,
64+
},
65+
}
66+
: {}
67+
),
4468
},
4569
});
4670

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
const BASE_URL = "https://generativelanguage.googleapis.com";
2-
const VERSION_PATH = "/v1";
2+
const VERSION_PATH = "/v1beta";
3+
4+
const MODEL_METHODS = {
5+
GENERATE_CONTENT: "generateContent",
6+
};
37

48
export default {
59
BASE_URL,
610
VERSION_PATH,
11+
MODEL_METHODS,
712
};
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { ConfigurationError } from "@pipedream/platform";
2+
3+
function emptyStrToUndefined(value) {
4+
const trimmed = typeof(value) === "string" && value.trim();
5+
return trimmed === ""
6+
? undefined
7+
: value;
8+
}
9+
10+
function parse(value) {
11+
const valueToParse = emptyStrToUndefined(value);
12+
if (typeof(valueToParse) === "object" || valueToParse === undefined) {
13+
return valueToParse;
14+
}
15+
try {
16+
return JSON.parse(valueToParse);
17+
} catch (e) {
18+
throw new ConfigurationError("Make sure the custom expression contains a valid object");
19+
}
20+
}
21+
22+
export default {
23+
parse,
24+
};

components/google_gemini/google_gemini.app.mjs

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,11 @@ export default {
3434
type: "string",
3535
label: "Model",
3636
description: "The model to use for content generation",
37-
async options({ prevContext: { pageToken } }) {
37+
reloadProps: true,
38+
async options({
39+
prevContext: { pageToken },
40+
filter = (model) => model,
41+
}) {
3842
if (pageToken === null) {
3943
return [];
4044
}
@@ -46,13 +50,16 @@ export default {
4650
pageToken,
4751
},
4852
});
49-
const options = models.map(({
50-
name: value,
51-
displayName: label,
52-
}) => ({
53-
label,
54-
value,
55-
}));
53+
54+
const options = models
55+
.filter(filter)
56+
.map(({
57+
name: value,
58+
displayName: label,
59+
}) => ({
60+
label,
61+
value,
62+
}));
5663

5764
return {
5865
options,
@@ -62,6 +69,14 @@ export default {
6269
};
6370
},
6471
},
72+
responseFormat: {
73+
type: "boolean",
74+
label: "JSON Output",
75+
description: "Enable to receive responses in structured JSON format instead of plain text. Useful for automated processing, data extraction, or when you need to parse the response programmatically. You can optionally define a specific schema for the response structure.",
76+
optional: true,
77+
default: false,
78+
reloadProps: true,
79+
},
6580
},
6681
methods: {
6782
getUrl(path) {
@@ -99,7 +114,7 @@ export default {
99114
? model
100115
: `models/${model}`;
101116
return this.post({
102-
path: `/${pathPrefix}:generateContent`,
117+
path: `/${pathPrefix}:${constants.MODEL_METHODS.GENERATE_CONTENT}`,
103118
...args,
104119
});
105120
},
@@ -109,5 +124,13 @@ export default {
109124
...args,
110125
});
111126
},
127+
getModel({
128+
model, ...args
129+
} = {}) {
130+
return this.makeRequest({
131+
path: `/${model}`,
132+
...args,
133+
});
134+
},
112135
},
113136
};

components/google_gemini/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@pipedream/google_gemini",
3-
"version": "0.2.0",
3+
"version": "0.2.1",
44
"description": "Pipedream Google Gemini Components",
55
"main": "google_gemini.app.mjs",
66
"keywords": [
@@ -13,6 +13,6 @@
1313
"access": "public"
1414
},
1515
"dependencies": {
16-
"@pipedream/platform": "^3.0.0"
16+
"@pipedream/platform": "^3.0.3"
1717
}
1818
}

0 commit comments

Comments
 (0)