@divyacon/piece-google-gemini 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.MIT-AP +24 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +41 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/actions/chat-gemini.action.d.ts +6 -0
- package/dist/lib/actions/chat-gemini.action.d.ts.map +1 -0
- package/dist/lib/actions/chat-gemini.action.js +74 -0
- package/dist/lib/actions/chat-gemini.action.js.map +1 -0
- package/dist/lib/actions/create-video.action.d.ts +11 -0
- package/dist/lib/actions/create-video.action.d.ts.map +1 -0
- package/dist/lib/actions/create-video.action.js +183 -0
- package/dist/lib/actions/create-video.action.js.map +1 -0
- package/dist/lib/actions/generate-content-from-image.action.d.ts +6 -0
- package/dist/lib/actions/generate-content-from-image.action.d.ts.map +1 -0
- package/dist/lib/actions/generate-content-from-image.action.js +66 -0
- package/dist/lib/actions/generate-content-from-image.action.js.map +1 -0
- package/dist/lib/actions/generate-content-with-file-search.d.ts +7 -0
- package/dist/lib/actions/generate-content-with-file-search.d.ts.map +1 -0
- package/dist/lib/actions/generate-content-with-file-search.js +83 -0
- package/dist/lib/actions/generate-content-with-file-search.js.map +1 -0
- package/dist/lib/actions/generate-content.action.d.ts +7 -0
- package/dist/lib/actions/generate-content.action.d.ts.map +1 -0
- package/dist/lib/actions/generate-content.action.js +192 -0
- package/dist/lib/actions/generate-content.action.js.map +1 -0
- package/dist/lib/actions/text-to-speech.action.d.ts +7 -0
- package/dist/lib/actions/text-to-speech.action.d.ts.map +1 -0
- package/dist/lib/actions/text-to-speech.action.js +137 -0
- package/dist/lib/actions/text-to-speech.action.js.map +1 -0
- package/dist/lib/auth.d.ts +2 -0
- package/dist/lib/auth.d.ts.map +1 -0
- package/dist/lib/auth.js +42 -0
- package/dist/lib/auth.js.map +1 -0
- package/dist/lib/common/common.d.ts +49 -0
- package/dist/lib/common/common.d.ts.map +1 -0
- package/dist/lib/common/common.js +84 -0
- package/dist/lib/common/common.js.map +1 -0
- package/dist/lib/output-schemas.d.ts +7 -0
- package/dist/lib/output-schemas.d.ts.map +1 -0
- package/dist/lib/output-schemas.js +129 -0
- package/dist/lib/output-schemas.js.map +1 -0
- package/package.json +51 -4
- package/src/index.ts +35 -0
- package/src/lib/actions/chat-gemini.action.ts +78 -0
- package/src/lib/actions/create-video.action.ts +206 -0
- package/src/lib/actions/generate-content-from-image.action.ts +67 -0
- package/src/lib/actions/generate-content-with-file-search.ts +82 -0
- package/src/lib/actions/generate-content.action.ts +199 -0
- package/src/lib/actions/text-to-speech.action.ts +143 -0
- package/src/lib/auth.ts +38 -0
- package/src/lib/common/common.ts +121 -0
- package/src/lib/output-schemas.ts +131 -0
- package/README.md +0 -3
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.textToSpeechActionOutputSchema = exports.generateContentWithFilesearchActionOutputSchema = exports.generateContentFromImageActionOutputSchema = exports.chatGeminiActionOutputSchema = exports.generateContentActionOutputSchema = void 0;
|
|
4
|
+
exports.generateContentActionOutputSchema = {
|
|
5
|
+
fields: [
|
|
6
|
+
{
|
|
7
|
+
key: 'text',
|
|
8
|
+
label: 'Generated Content',
|
|
9
|
+
value: '',
|
|
10
|
+
},
|
|
11
|
+
],
|
|
12
|
+
};
|
|
13
|
+
exports.chatGeminiActionOutputSchema = {
|
|
14
|
+
fields: [
|
|
15
|
+
{
|
|
16
|
+
key: 'response',
|
|
17
|
+
label: 'Response',
|
|
18
|
+
value: 'response',
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
key: 'history',
|
|
22
|
+
label: 'Conversation History',
|
|
23
|
+
value: 'history',
|
|
24
|
+
listItems: [
|
|
25
|
+
{
|
|
26
|
+
key: 'role',
|
|
27
|
+
label: 'Role',
|
|
28
|
+
value: 'role',
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
key: 'text',
|
|
32
|
+
label: 'Text',
|
|
33
|
+
value: 'parts[0].text',
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
};
|
|
39
|
+
exports.generateContentFromImageActionOutputSchema = {
|
|
40
|
+
fields: [
|
|
41
|
+
{
|
|
42
|
+
key: 'text',
|
|
43
|
+
label: 'Generated Text',
|
|
44
|
+
value: 'text',
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
key: 'modelVersion',
|
|
48
|
+
label: 'Model Version',
|
|
49
|
+
value: 'raw.modelVersion',
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
key: 'finishReason',
|
|
53
|
+
label: 'Finish Reason',
|
|
54
|
+
value: 'raw.candidates[0].finishReason',
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
key: 'candidates',
|
|
58
|
+
label: 'Candidates',
|
|
59
|
+
value: 'raw.candidates',
|
|
60
|
+
listItems: [
|
|
61
|
+
{
|
|
62
|
+
key: 'text',
|
|
63
|
+
label: 'Text',
|
|
64
|
+
value: 'content.parts[0].text',
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
key: 'role',
|
|
68
|
+
label: 'Role',
|
|
69
|
+
value: 'content.role',
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
key: 'finishReason',
|
|
73
|
+
label: 'Finish Reason',
|
|
74
|
+
value: 'finishReason',
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
key: 'usageMetadata',
|
|
80
|
+
label: 'Usage Metadata',
|
|
81
|
+
value: 'raw.usageMetadata',
|
|
82
|
+
children: [
|
|
83
|
+
{
|
|
84
|
+
key: 'promptTokenCount',
|
|
85
|
+
label: 'Prompt Tokens',
|
|
86
|
+
value: 'promptTokenCount',
|
|
87
|
+
format: 'number',
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
key: 'candidatesTokenCount',
|
|
91
|
+
label: 'Candidates Tokens',
|
|
92
|
+
value: 'candidatesTokenCount',
|
|
93
|
+
format: 'number',
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
key: 'totalTokenCount',
|
|
97
|
+
label: 'Total Tokens',
|
|
98
|
+
value: 'totalTokenCount',
|
|
99
|
+
format: 'number',
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
key: 'serviceTier',
|
|
103
|
+
label: 'Service Tier',
|
|
104
|
+
value: 'serviceTier',
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
},
|
|
108
|
+
],
|
|
109
|
+
};
|
|
110
|
+
exports.generateContentWithFilesearchActionOutputSchema = {
|
|
111
|
+
fields: [
|
|
112
|
+
{
|
|
113
|
+
key: 'text',
|
|
114
|
+
label: 'Generated Content',
|
|
115
|
+
value: '',
|
|
116
|
+
},
|
|
117
|
+
],
|
|
118
|
+
};
|
|
119
|
+
exports.textToSpeechActionOutputSchema = {
|
|
120
|
+
fields: [
|
|
121
|
+
{
|
|
122
|
+
key: 'audioFile',
|
|
123
|
+
label: 'Audio File URL',
|
|
124
|
+
value: '',
|
|
125
|
+
format: 'url',
|
|
126
|
+
},
|
|
127
|
+
],
|
|
128
|
+
};
|
|
129
|
+
//# sourceMappingURL=output-schemas.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output-schemas.js","sourceRoot":"","sources":["../../src/lib/output-schemas.ts"],"names":[],"mappings":";;;AAIa,QAAA,iCAAiC,GAAiB;IAC9D,MAAM,EAAE;QACP;YACC,GAAG,EAAE,MAAM;YACX,KAAK,EAAE,mBAAmB;YAC1B,KAAK,EAAE,EAAE;SACT;KACD;CACD,CAAC;AAEW,QAAA,4BAA4B,GAAiB;IACxD,MAAM,EAAE;QACN;YACE,GAAG,EAAE,UAAU;YACf,KAAK,EAAE,UAAU;YACjB,KAAK,EAAE,UAAU;SAClB;QACD;YACE,GAAG,EAAE,SAAS;YACd,KAAK,EAAE,sBAAsB;YAC7B,KAAK,EAAE,SAAS;YAChB,SAAS,EAAE;gBACT;oBACE,GAAG,EAAE,MAAM;oBACX,KAAK,EAAE,MAAM;oBACb,KAAK,EAAE,MAAM;iBACd;gBACD;oBACE,GAAG,EAAE,MAAM;oBACX,KAAK,EAAE,MAAM;oBACb,KAAK,EAAE,eAAe;iBACvB;aACF;SACF;KACF;CACF,CAAC;AAEW,QAAA,0CAA0C,GAAiB;IACtE,MAAM,EAAE;QACN;YACE,GAAG,EAAE,MAAM;YACX,KAAK,EAAE,gBAAgB;YACvB,KAAK,EAAE,MAAM;SACd;QACD;YACE,GAAG,EAAE,cAAc;YACnB,KAAK,EAAE,eAAe;YACtB,KAAK,EAAE,kBAAkB;SAC1B;QACD;YACE,GAAG,EAAE,cAAc;YACnB,KAAK,EAAE,eAAe;YACtB,KAAK,EAAE,gCAAgC;SACxC;QACD;YACE,GAAG,EAAE,YAAY;YACjB,KAAK,EAAE,YAAY;YACnB,KAAK,EAAE,gBAAgB;YACvB,SAAS,EAAE;gBACT;oBACE,GAAG,EAAE,MAAM;oBACX,KAAK,EAAE,MAAM;oBACb,KAAK,EAAE,uBAAuB;iBAC/B;gBACD;oBACE,GAAG,EAAE,MAAM;oBACX,KAAK,EAAE,MAAM;oBACb,KAAK,EAAE,cAAc;iBACtB;gBACD;oBACE,GAAG,EAAE,cAAc;oBACnB,KAAK,EAAE,eAAe;oBACtB,KAAK,EAAE,cAAc;iBACtB;aACF;SACF;QACD;YACE,GAAG,EAAE,eAAe;YACpB,KAAK,EAAE,gBAAgB;YACvB,KAAK,EAAE,mBAAmB;YAC1B,QAAQ,EAAE;gBACR;oBACE,GAAG,EAAE,kBAAkB;oBACvB,KAAK,EAAE,eAAe;oBACtB,KAAK,EAAE,kBAAkB;oBACzB,MAAM,EAAE,QAAQ;iBACjB;gBACD;oBACE,GAAG,EAAE,sBAAsB;oBAC3B,KAAK,EAAE,mBAAmB;oBAC1B,KAAK,EAAE,sBAAsB;oBAC7B,MAAM,EAAE,QAAQ;iBACjB;gBACD;oBACE,GAAG,EAAE,iBAAiB;oBACtB,KAAK,EAAE,cAAc;oBACrB,KAAK,EAAE,iBAAiB;oBACxB,MAAM,EAAE,QAAQ;iBACjB;gBACD;oBACE,GAAG,EAAE,aAAa;oBAClB,KAAK,EAAE,cAAc;oBACrB,KAAK,EAAE,aAAa;iBACrB;aACF;SACF;KACF;CACF,CAAC;AAEW,QAAA,+CAA+C,GAAiB;IAC3E,MAAM,EAAE;QACN;YACE,GAAG,EAAE,MAAM;YACX,KAAK,EAAE,mBAAmB;YAC1B,KAAK,EAAE,EAAE;SACV;KACF;CACF,CAAC;AAEW,QAAA,8BAA8B,GAAiB;IAC1D,MAAM,EAAE;QACN;YACE,GAAG,EAAE,WAAW;YAChB,KAAK,EAAE,gBAAgB;YACvB,KAAK,EAAE,EAAE;YACT,MAAM,EAAE,KAAK;SACd;KACF;CACF,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,53 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@divyacon/piece-google-gemini",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "DivyaCon google-gemini connector",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"require": "./dist/index.js",
|
|
11
|
+
"default": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"src",
|
|
17
|
+
"LICENSE.MIT-AP"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsc -p tsconfig.json",
|
|
21
|
+
"vendor": "node ../scripts/vendor-ap.mjs piece-google-gemini"
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@agnocon/core-utils": "0.2.0",
|
|
25
|
+
"@agnocon/piece-types": "0.3.1",
|
|
26
|
+
"@agnocon/pieces-common": "0.12.8",
|
|
27
|
+
"@agnocon/pieces-framework": "0.36.0",
|
|
28
|
+
"tslib": "2.6.2",
|
|
29
|
+
"@google/genai": "1.29.0",
|
|
30
|
+
"@google/generative-ai": "0.21.0",
|
|
31
|
+
"mime-types": "2.1.35",
|
|
32
|
+
"wav": "^1.0.2",
|
|
33
|
+
"zod": "4.3.6"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/node": "20.17.6",
|
|
37
|
+
"typescript": "5.5.4",
|
|
38
|
+
"@types/wav": "^1.0.4",
|
|
39
|
+
"@types/mime-types": "2.1.1"
|
|
40
|
+
},
|
|
41
|
+
"license": "MIT",
|
|
42
|
+
"agnocon": {
|
|
43
|
+
"originVersion": "0.1.14"
|
|
44
|
+
},
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"access": "public",
|
|
47
|
+
"tag": "latest"
|
|
48
|
+
},
|
|
49
|
+
"keywords": [
|
|
50
|
+
"agnocon",
|
|
51
|
+
"connector"
|
|
52
|
+
]
|
|
53
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { createCustomApiCallAction } from '@agnocon/pieces-common';
|
|
2
|
+
import { createPiece } from '@agnocon/pieces-framework';
|
|
3
|
+
import { PieceCategory } from '@agnocon/pieces-framework';
|
|
4
|
+
import { chatGemini } from './lib/actions/chat-gemini.action';
|
|
5
|
+
import { createVideoAction } from './lib/actions/create-video.action';
|
|
6
|
+
import { generateContentFromImageAction } from './lib/actions/generate-content-from-image.action';
|
|
7
|
+
import { generateContentAction } from './lib/actions/generate-content.action';
|
|
8
|
+
import { textToSpeechAction } from './lib/actions/text-to-speech.action';
|
|
9
|
+
import { generateContentWithFileSearchAction } from './lib/actions/generate-content-with-file-search';
|
|
10
|
+
import { googleGeminiAuth } from './lib/auth';
|
|
11
|
+
|
|
12
|
+
export const googleGemini = createPiece({
|
|
13
|
+
displayName: 'Google Gemini',
|
|
14
|
+
auth: googleGeminiAuth,
|
|
15
|
+
description: 'Use the new Gemini models from Google',
|
|
16
|
+
minimumSupportedRelease: '0.30.0',
|
|
17
|
+
logoUrl: 'https://cdn.activepieces.com/pieces/google-gemini.png',
|
|
18
|
+
categories: [PieceCategory.ARTIFICIAL_INTELLIGENCE],
|
|
19
|
+
authors: ["pfernandez98","kishanprmr","MoShizzle","AbdulTheActivePiecer","abuaboud"],
|
|
20
|
+
actions: [
|
|
21
|
+
generateContentAction,
|
|
22
|
+
generateContentWithFileSearchAction,
|
|
23
|
+
generateContentFromImageAction,
|
|
24
|
+
chatGemini,
|
|
25
|
+
textToSpeechAction,
|
|
26
|
+
createVideoAction,
|
|
27
|
+
createCustomApiCallAction({
|
|
28
|
+
baseUrl: () => 'https://generativelanguage.googleapis.com/v1beta',
|
|
29
|
+
auth: googleGeminiAuth,
|
|
30
|
+
authMapping: async (auth) => ({ key: auth.secret_text }),
|
|
31
|
+
authLocation: 'queryParams',
|
|
32
|
+
}),
|
|
33
|
+
],
|
|
34
|
+
triggers: [],
|
|
35
|
+
});
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Content, GoogleGenerativeAI } from '@google/generative-ai';
|
|
2
|
+
import {
|
|
3
|
+
Property,
|
|
4
|
+
StoreScope,
|
|
5
|
+
createAction,
|
|
6
|
+
} from '@agnocon/pieces-framework';
|
|
7
|
+
import mime from 'mime-types';
|
|
8
|
+
import * as z from 'zod/mini'
|
|
9
|
+
import { googleGeminiAuth } from '../auth';
|
|
10
|
+
import { defaultLLM, getGeminiModelOptions } from '../common/common';
|
|
11
|
+
import { propsValidation } from '@agnocon/pieces-common';
|
|
12
|
+
import { chatGeminiActionOutputSchema } from '../output-schemas';
|
|
13
|
+
|
|
14
|
+
export const chatGemini = createAction({
|
|
15
|
+
audience: 'both',
|
|
16
|
+
auth: googleGeminiAuth,
|
|
17
|
+
name: 'chat_gemini',
|
|
18
|
+
displayName: 'Chat Gemini',
|
|
19
|
+
description: 'Chat with Google Gemini',
|
|
20
|
+
aiMetadata: { description: 'Sends a prompt as one turn of a Gemini chat and returns the reply, with two modes: supply a memory key (max 128 characters) to load and persist the conversation history in project storage so later runs remember earlier turns, or leave it empty for a stateless single turn. Pick it over generate_content when the exchange spans multiple runs and must retain context; generate_content is the better choice for one-shot generation or when a built-in tool such as Google Search or File Search is needed. Not idempotent: each call produces a new reply and, when a memory key is set, rewrites the stored history.', idempotent: false },
|
|
21
|
+
props: {
|
|
22
|
+
model: Property.Dropdown({
|
|
23
|
+
displayName: 'Model',
|
|
24
|
+
required: true,
|
|
25
|
+
description: 'The model which will generate the completion',
|
|
26
|
+
refreshers: [],
|
|
27
|
+
auth: googleGeminiAuth,
|
|
28
|
+
defaultValue: defaultLLM,
|
|
29
|
+
options: async ({ auth }) => getGeminiModelOptions({ auth }),
|
|
30
|
+
}),
|
|
31
|
+
prompt: Property.LongText({
|
|
32
|
+
displayName: 'Prompt',
|
|
33
|
+
required: true,
|
|
34
|
+
description: 'The prompt to generate content from.',
|
|
35
|
+
}),
|
|
36
|
+
memoryKey: Property.ShortText({
|
|
37
|
+
displayName: 'Memory Key',
|
|
38
|
+
description:
|
|
39
|
+
'A memory key that will keep the chat history. Keep it empty to leave Gemini without memory of previous messages.',
|
|
40
|
+
required: false,
|
|
41
|
+
}),
|
|
42
|
+
},
|
|
43
|
+
outputSchema: chatGeminiActionOutputSchema,
|
|
44
|
+
async run({ auth, propsValue, store }) {
|
|
45
|
+
await propsValidation.validateZod(propsValue, {
|
|
46
|
+
memoryKey: z.optional(z.string().check(z.maxLength(128))),
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const { model, prompt, memoryKey } = propsValue;
|
|
50
|
+
const genAI = new GoogleGenerativeAI(auth.secret_text);
|
|
51
|
+
const geminiModel = genAI.getGenerativeModel({ model });
|
|
52
|
+
let history: Content[] = [];
|
|
53
|
+
|
|
54
|
+
if (memoryKey) {
|
|
55
|
+
const storedHistory = await store.get(memoryKey, StoreScope.PROJECT);
|
|
56
|
+
if (Array.isArray(storedHistory)) {
|
|
57
|
+
history = storedHistory;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const chat = geminiModel.startChat({
|
|
62
|
+
history: history,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const result = await chat.sendMessage(prompt);
|
|
66
|
+
const responseText = result.response.text();
|
|
67
|
+
|
|
68
|
+
if (memoryKey) {
|
|
69
|
+
const updatedHistory = await chat.getHistory();
|
|
70
|
+
await store.put(memoryKey, updatedHistory, StoreScope.PROJECT);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
response: responseText,
|
|
75
|
+
history: history,
|
|
76
|
+
};
|
|
77
|
+
},
|
|
78
|
+
});
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { createAction, Property } from '@agnocon/pieces-framework';
|
|
2
|
+
import { httpClient, HttpMethod } from '@agnocon/pieces-common';
|
|
3
|
+
import { googleGeminiAuth } from '../auth';
|
|
4
|
+
import { getGeminiVideoModelOptions } from '../common/common';
|
|
5
|
+
import { GoogleGenAI } from '@google/genai';
|
|
6
|
+
import mime from 'mime-types';
|
|
7
|
+
|
|
8
|
+
export const createVideoAction = createAction({
|
|
9
|
+
audience: 'both',
|
|
10
|
+
name: 'create_video',
|
|
11
|
+
auth: googleGeminiAuth,
|
|
12
|
+
displayName: 'Create Video',
|
|
13
|
+
description: 'Generate a video from a text prompt using Google Veo models.',
|
|
14
|
+
aiMetadata: { description: 'Generates a short video with a Google Veo model from a text prompt, optionally seeded by a start image (image-to-video) or by both a start and end image (interpolation between those frames), polling the long-running generation job for up to 10 minutes before returning the finished MP4 as a file. Use it whenever the required output is video; text goes through generate_content and speech through text-to-speech. Note the model-dependent limits: 1080p needs Veo 3.0 or newer, 4K needs Veo 3.1, and both require an 8-second duration. Not idempotent: each call renders a new video.', idempotent: false },
|
|
15
|
+
props: {
|
|
16
|
+
model: Property.Dropdown({
|
|
17
|
+
displayName: 'Model',
|
|
18
|
+
required: true,
|
|
19
|
+
auth: googleGeminiAuth,
|
|
20
|
+
refreshers: [],
|
|
21
|
+
defaultValue: 'veo-3.1-generate-preview',
|
|
22
|
+
options: async ({ auth }) => getGeminiVideoModelOptions({ auth }),
|
|
23
|
+
}),
|
|
24
|
+
prompt: Property.LongText({
|
|
25
|
+
displayName: 'Prompt',
|
|
26
|
+
description:
|
|
27
|
+
'Describe the video you want to generate. Be specific about the scene, motion, style, and lighting for best results.',
|
|
28
|
+
required: true,
|
|
29
|
+
}),
|
|
30
|
+
image: Property.File({
|
|
31
|
+
displayName: 'Start Image',
|
|
32
|
+
description:
|
|
33
|
+
'Optional image to use as the first frame of the video (image-to-video).',
|
|
34
|
+
required: false,
|
|
35
|
+
}),
|
|
36
|
+
lastFrame: Property.File({
|
|
37
|
+
displayName: 'End Image',
|
|
38
|
+
description:
|
|
39
|
+
'Optional image to use as the last frame. Use together with Start Image to generate an interpolation video.',
|
|
40
|
+
required: false,
|
|
41
|
+
}),
|
|
42
|
+
aspectRatio: Property.StaticDropdown({
|
|
43
|
+
displayName: 'Aspect Ratio',
|
|
44
|
+
required: true,
|
|
45
|
+
defaultValue: '16:9',
|
|
46
|
+
options: {
|
|
47
|
+
disabled: false,
|
|
48
|
+
options: [
|
|
49
|
+
{ label: '16:9 (Landscape)', value: '16:9' },
|
|
50
|
+
{ label: '9:16 (Portrait)', value: '9:16' },
|
|
51
|
+
],
|
|
52
|
+
},
|
|
53
|
+
}),
|
|
54
|
+
durationSeconds: Property.Dropdown({
|
|
55
|
+
displayName: 'Duration (seconds)',
|
|
56
|
+
description: 'Must be 8 when using 1080p, 4K, or reference images.',
|
|
57
|
+
required: false,
|
|
58
|
+
auth: googleGeminiAuth,
|
|
59
|
+
refreshers: ['model'],
|
|
60
|
+
defaultValue: 8,
|
|
61
|
+
options: async ({ model }) => {
|
|
62
|
+
const isVeo2 = (model as string)?.startsWith('veo-2');
|
|
63
|
+
if (isVeo2) {
|
|
64
|
+
return {
|
|
65
|
+
disabled: false,
|
|
66
|
+
options: [
|
|
67
|
+
{ label: '5 seconds', value: 5 },
|
|
68
|
+
{ label: '6 seconds', value: 6 },
|
|
69
|
+
{ label: '7 seconds', value: 7 },
|
|
70
|
+
{ label: '8 seconds', value: 8 },
|
|
71
|
+
],
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
disabled: false,
|
|
76
|
+
options: [
|
|
77
|
+
{ label: '4 seconds', value: 4 },
|
|
78
|
+
{ label: '6 seconds', value: 6 },
|
|
79
|
+
{ label: '8 seconds', value: 8 },
|
|
80
|
+
],
|
|
81
|
+
};
|
|
82
|
+
},
|
|
83
|
+
}),
|
|
84
|
+
resolution: Property.Dropdown({
|
|
85
|
+
displayName: 'Resolution',
|
|
86
|
+
description:
|
|
87
|
+
'1080p is available on Veo 3.0+ only. 4K is only available on Veo 3.1 models. 1080p and 4K require 8-second duration.',
|
|
88
|
+
required: false,
|
|
89
|
+
auth: googleGeminiAuth,
|
|
90
|
+
refreshers: ['model'],
|
|
91
|
+
defaultValue: '720p',
|
|
92
|
+
options: async ({ model }) => {
|
|
93
|
+
const modelStr = model as string;
|
|
94
|
+
const isVeo2 = modelStr?.startsWith('veo-2');
|
|
95
|
+
const is31 = modelStr?.startsWith('veo-3.1');
|
|
96
|
+
return {
|
|
97
|
+
disabled: false,
|
|
98
|
+
options: [
|
|
99
|
+
{ label: '720p', value: '720p' },
|
|
100
|
+
...(isVeo2 ? [] : [{ label: '1080p', value: '1080p' }]),
|
|
101
|
+
...(is31 ? [{ label: '4K', value: '4k' }] : []),
|
|
102
|
+
],
|
|
103
|
+
};
|
|
104
|
+
},
|
|
105
|
+
}),
|
|
106
|
+
personGeneration: Property.StaticDropdown({
|
|
107
|
+
displayName: 'Person Generation',
|
|
108
|
+
description:
|
|
109
|
+
'Controls whether people or faces can appear in the generated video.',
|
|
110
|
+
required: false,
|
|
111
|
+
defaultValue: 'allow_adult',
|
|
112
|
+
options: {
|
|
113
|
+
disabled: false,
|
|
114
|
+
options: [
|
|
115
|
+
{ label: 'Allow Adults Only (default)', value: 'allow_adult' },
|
|
116
|
+
{ label: 'Allow All Ages (requires allowlist)', value: 'allow_all' },
|
|
117
|
+
{ label: "Don't Allow People", value: 'dont_allow' },
|
|
118
|
+
],
|
|
119
|
+
},
|
|
120
|
+
}),
|
|
121
|
+
},
|
|
122
|
+
async run(context) {
|
|
123
|
+
const {
|
|
124
|
+
model,
|
|
125
|
+
prompt,
|
|
126
|
+
image,
|
|
127
|
+
lastFrame,
|
|
128
|
+
aspectRatio,
|
|
129
|
+
durationSeconds,
|
|
130
|
+
resolution,
|
|
131
|
+
personGeneration,
|
|
132
|
+
} = context.propsValue;
|
|
133
|
+
|
|
134
|
+
// API reference: https://docs.cloud.google.com/vertex-ai/generative-ai/docs/model-reference/veo-video-generation#sample-request
|
|
135
|
+
|
|
136
|
+
const ai = new GoogleGenAI({ apiKey: context.auth.secret_text });
|
|
137
|
+
|
|
138
|
+
let operation = await ai.models.generateVideos({
|
|
139
|
+
model,
|
|
140
|
+
prompt,
|
|
141
|
+
...(image
|
|
142
|
+
? {
|
|
143
|
+
image: {
|
|
144
|
+
imageBytes: image.base64,
|
|
145
|
+
mimeType:
|
|
146
|
+
mime.lookup(image.extension || image.filename) || 'image/jpeg',
|
|
147
|
+
},
|
|
148
|
+
}
|
|
149
|
+
: {}),
|
|
150
|
+
config: {
|
|
151
|
+
aspectRatio,
|
|
152
|
+
numberOfVideos: 1,
|
|
153
|
+
durationSeconds,
|
|
154
|
+
resolution,
|
|
155
|
+
personGeneration,
|
|
156
|
+
...(lastFrame
|
|
157
|
+
? {
|
|
158
|
+
lastFrame: {
|
|
159
|
+
imageBytes: lastFrame.base64,
|
|
160
|
+
mimeType:
|
|
161
|
+
mime.lookup(lastFrame.extension || lastFrame.filename) ||
|
|
162
|
+
'image/jpeg',
|
|
163
|
+
},
|
|
164
|
+
}
|
|
165
|
+
: {}),
|
|
166
|
+
},
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
const maxWaitMs = 10 * 60 * 1000; // 10 minutes
|
|
170
|
+
const startTime = Date.now();
|
|
171
|
+
while (!operation.done) {
|
|
172
|
+
if (Date.now() - startTime > maxWaitMs) {
|
|
173
|
+
throw new Error('Video generation timed out after 10 minutes.');
|
|
174
|
+
}
|
|
175
|
+
await new Promise((resolve) => setTimeout(resolve, 5000));
|
|
176
|
+
operation = await ai.operations.getVideosOperation({ operation });
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const video = operation.response?.generatedVideos?.[0]?.video;
|
|
180
|
+
|
|
181
|
+
if (!video) {
|
|
182
|
+
throw new Error('No video data returned from model response.');
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
let videoBuffer: Buffer;
|
|
186
|
+
|
|
187
|
+
if (video.videoBytes) {
|
|
188
|
+
videoBuffer = Buffer.from(video.videoBytes, 'base64');
|
|
189
|
+
} else if (video.uri) {
|
|
190
|
+
const response = await httpClient.sendRequest<ArrayBuffer>({
|
|
191
|
+
method: HttpMethod.GET,
|
|
192
|
+
url: video.uri,
|
|
193
|
+
queryParams: { key: context.auth.secret_text },
|
|
194
|
+
responseType: 'arraybuffer',
|
|
195
|
+
});
|
|
196
|
+
videoBuffer = Buffer.from(response.body);
|
|
197
|
+
} else {
|
|
198
|
+
throw new Error('No video bytes or URI in model response.');
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return await context.files.write({
|
|
202
|
+
data: videoBuffer,
|
|
203
|
+
fileName: 'video.mp4',
|
|
204
|
+
});
|
|
205
|
+
},
|
|
206
|
+
});
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { GoogleGenerativeAI } from '@google/generative-ai';
|
|
2
|
+
import mime from 'mime-types';
|
|
3
|
+
import {
|
|
4
|
+
Property,
|
|
5
|
+
createAction,
|
|
6
|
+
} from '@agnocon/pieces-framework';
|
|
7
|
+
import { googleGeminiAuth } from '../auth';
|
|
8
|
+
import { defaultLLM, getGeminiModelOptions } from '../common/common';
|
|
9
|
+
import { generateContentFromImageActionOutputSchema } from '../output-schemas';
|
|
10
|
+
|
|
11
|
+
export const generateContentFromImageAction = createAction({
|
|
12
|
+
audience: 'both',
|
|
13
|
+
description:
|
|
14
|
+
'Generate content using Google Gemini using the "gemini-pro-vision" model',
|
|
15
|
+
aiMetadata: { description: 'Sends an image together with a prompt to a vision-capable Gemini model and returns the generated text describing or answering questions about that image, covering captioning, text extraction, and visual question answering. This is the only Gemini action here that accepts image input, so pick it over generate_content whenever a picture is part of the question; the image is required and is sent inline, so keep it small. Not idempotent: each call produces a fresh completion.', idempotent: false },
|
|
16
|
+
displayName: 'Generate Content from Image',
|
|
17
|
+
name: 'generate_content_from_image',
|
|
18
|
+
auth: googleGeminiAuth,
|
|
19
|
+
props: {
|
|
20
|
+
prompt: Property.LongText({
|
|
21
|
+
displayName: 'Prompt',
|
|
22
|
+
required: true,
|
|
23
|
+
description: 'The prompt to generate content from.',
|
|
24
|
+
}),
|
|
25
|
+
image: Property.File({
|
|
26
|
+
displayName: 'Image',
|
|
27
|
+
required: true,
|
|
28
|
+
description: 'The image to generate content from.'
|
|
29
|
+
}),
|
|
30
|
+
model: Property.Dropdown({
|
|
31
|
+
displayName: 'Model',
|
|
32
|
+
auth: googleGeminiAuth,
|
|
33
|
+
required: true,
|
|
34
|
+
description: 'The model which will generate the completion',
|
|
35
|
+
refreshers: [],
|
|
36
|
+
defaultValue: defaultLLM,
|
|
37
|
+
options: async ({ auth }) =>
|
|
38
|
+
getGeminiModelOptions({ auth }),
|
|
39
|
+
}),
|
|
40
|
+
},
|
|
41
|
+
outputSchema: generateContentFromImageActionOutputSchema,
|
|
42
|
+
|
|
43
|
+
async run({ auth, propsValue }) {
|
|
44
|
+
const { image, model, prompt } = propsValue;
|
|
45
|
+
const mimeType =
|
|
46
|
+
mime.lookup(image.extension || image.filename) ||
|
|
47
|
+
`image/${image.extension}`;
|
|
48
|
+
|
|
49
|
+
const genAI = new GoogleGenerativeAI(auth.secret_text);
|
|
50
|
+
const generativeModel = genAI.getGenerativeModel({ model });
|
|
51
|
+
const result = await generativeModel.generateContent([
|
|
52
|
+
prompt,
|
|
53
|
+
{
|
|
54
|
+
inlineData: {
|
|
55
|
+
data: image.base64,
|
|
56
|
+
mimeType,
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
]);
|
|
60
|
+
|
|
61
|
+
const response = await result.response;
|
|
62
|
+
return {
|
|
63
|
+
text: response.text(),
|
|
64
|
+
raw: response,
|
|
65
|
+
};
|
|
66
|
+
},
|
|
67
|
+
});
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { googleGeminiAuth } from '../auth';
|
|
2
|
+
import { Property, createAction } from '@agnocon/pieces-framework';
|
|
3
|
+
import { defaultLLM, getGeminiModelOptions } from '../common/common';
|
|
4
|
+
import { GoogleGenAI } from '@google/genai';
|
|
5
|
+
import mime from 'mime-types';
|
|
6
|
+
import { generateContentWithFilesearchActionOutputSchema } from '../output-schemas';
|
|
7
|
+
|
|
8
|
+
export const generateContentWithFileSearchAction = createAction({
|
|
9
|
+
audience: 'both',
|
|
10
|
+
description: 'Generate content with file search functionality.',
|
|
11
|
+
aiMetadata: { description: 'Uploads one file to a newly created Gemini File Search store, waits for indexing to finish, then answers the prompt grounded on that document. Use it for one-shot question answering over a document supplied at run time; prefer generate_content when no file grounding is needed or when a different built-in tool such as Google Search or URL Context fits better. Both a file and a store display name are required. Not idempotent: every call creates another file search store and a fresh completion.', idempotent: false },
|
|
12
|
+
displayName: 'Generate Content with File Search',
|
|
13
|
+
name: 'generate_content_with_filesearch',
|
|
14
|
+
auth: googleGeminiAuth,
|
|
15
|
+
props: {
|
|
16
|
+
prompt: Property.LongText({
|
|
17
|
+
displayName: 'Prompt',
|
|
18
|
+
required: true,
|
|
19
|
+
description: 'The prompt to generate content from.',
|
|
20
|
+
}),
|
|
21
|
+
model: Property.Dropdown({
|
|
22
|
+
displayName: 'Model',
|
|
23
|
+
auth: googleGeminiAuth,
|
|
24
|
+
required: true,
|
|
25
|
+
description: 'The model which will generate the completion',
|
|
26
|
+
refreshers: [],
|
|
27
|
+
defaultValue: defaultLLM,
|
|
28
|
+
options: async ({ auth }) => getGeminiModelOptions({ auth }),
|
|
29
|
+
}),
|
|
30
|
+
fileStoreName: Property.ShortText({
|
|
31
|
+
displayName: 'File Store Name',
|
|
32
|
+
required: true,
|
|
33
|
+
}),
|
|
34
|
+
file: Property.File({
|
|
35
|
+
displayName: 'File',
|
|
36
|
+
required: true,
|
|
37
|
+
}),
|
|
38
|
+
},
|
|
39
|
+
outputSchema: generateContentWithFilesearchActionOutputSchema,
|
|
40
|
+
async run({ auth, propsValue }) {
|
|
41
|
+
const { file, fileStoreName, model, prompt } = propsValue;
|
|
42
|
+
|
|
43
|
+
const fileBlob = new Blob([Buffer.from(file.base64, 'base64')], {
|
|
44
|
+
type: mime.lookup(file.extension || file.filename) || undefined,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const genAI = new GoogleGenAI({ apiKey: auth.secret_text });
|
|
48
|
+
|
|
49
|
+
const fileSearchStore = await genAI.fileSearchStores.create({
|
|
50
|
+
config: { displayName: fileStoreName },
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
let operation = await genAI.fileSearchStores.uploadToFileSearchStore({
|
|
54
|
+
file: fileBlob,
|
|
55
|
+
fileSearchStoreName: fileSearchStore.name!,
|
|
56
|
+
config: {
|
|
57
|
+
displayName: file.filename,
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
while (!operation.done) {
|
|
62
|
+
await new Promise((resolve) => setTimeout(resolve, 5000));
|
|
63
|
+
operation = await genAI.operations.get({ operation });
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const response = await genAI.models.generateContent({
|
|
67
|
+
model: model,
|
|
68
|
+
contents: prompt,
|
|
69
|
+
config: {
|
|
70
|
+
tools: [
|
|
71
|
+
{
|
|
72
|
+
fileSearch: {
|
|
73
|
+
fileSearchStoreNames: [fileSearchStore.name!],
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
return response.text;
|
|
81
|
+
},
|
|
82
|
+
});
|