5
5
version: 1.3
6
6
license: MIT
7
7
description: A pipeline for generating text using Google's GenAI models in Open-WebUI.
8
- requirements: google-generativeai
8
+ requirements: google-genai
9
9
environment_variables: GOOGLE_API_KEY
10
10
"""
11
11
14
14
15
15
from pydantic import BaseModel , Field
16
16
17
- import google .generativeai as genai
18
- from google .generativeai .types import GenerationConfig
17
+ from google import genai
18
+ from google .genai import types
19
+ from PIL import Image
20
+ from io import BytesIO
21
+ import base64
19
22
20
23
21
24
class Pipeline :
@@ -24,8 +27,9 @@ class Pipeline:
24
27
class Valves (BaseModel ):
25
28
"""Options to change from the WebUI"""
26
29
27
- GOOGLE_API_KEY : str = ""
28
- USE_PERMISSIVE_SAFETY : bool = Field (default = False )
30
+ GOOGLE_API_KEY : str = Field (default = "" ,description = "Google Generative AI API key" )
31
+ USE_PERMISSIVE_SAFETY : bool = Field (default = False ,description = "Use permissive safety settings" )
32
+ GENERATE_IMAGE : bool = Field (default = False ,description = "Allow image generation" )
29
33
30
34
def __init__ (self ):
31
35
self .type = "manifold"
@@ -34,19 +38,20 @@ def __init__(self):
34
38
35
39
self .valves = self .Valves (** {
36
40
"GOOGLE_API_KEY" : os .getenv ("GOOGLE_API_KEY" , "" ),
37
- "USE_PERMISSIVE_SAFETY" : False
41
+ "USE_PERMISSIVE_SAFETY" : False ,
42
+ "GENERATE_IMAGE" : False
38
43
})
39
44
self .pipelines = []
40
45
41
- genai . configure ( api_key = self .valves .GOOGLE_API_KEY )
42
- self .update_pipelines ()
46
+ if self .valves .GOOGLE_API_KEY :
47
+ self .update_pipelines ()
43
48
44
49
async def on_startup (self ) -> None :
45
50
"""This function is called when the server is started."""
46
51
47
52
print (f"on_startup:{ __name__ } " )
48
- genai . configure ( api_key = self .valves .GOOGLE_API_KEY )
49
- self .update_pipelines ()
53
+ if self .valves .GOOGLE_API_KEY :
54
+ self .update_pipelines ()
50
55
51
56
async def on_shutdown (self ) -> None :
52
57
"""This function is called when the server is stopped."""
@@ -57,22 +62,23 @@ async def on_valves_updated(self) -> None:
57
62
"""This function is called when the valves are updated."""
58
63
59
64
print (f"on_valves_updated:{ __name__ } " )
60
- genai . configure ( api_key = self .valves .GOOGLE_API_KEY )
61
- self .update_pipelines ()
65
+ if self .valves .GOOGLE_API_KEY :
66
+ self .update_pipelines ()
62
67
63
68
def update_pipelines (self ) -> None :
64
69
"""Update the available models from Google GenAI"""
65
70
66
71
if self .valves .GOOGLE_API_KEY :
72
+ client = genai .Client (api_key = self .valves .GOOGLE_API_KEY )
67
73
try :
68
- models = genai . list_models ()
74
+ models = client . models . list ()
69
75
self .pipelines = [
70
76
{
71
77
"id" : model .name [7 :], # the "models/" part messeses up the URL
72
78
"name" : model .display_name ,
73
79
}
74
80
for model in models
75
- if "generateContent" in model .supported_generation_methods
81
+ if "generateContent" in model .supported_actions
76
82
if model .name [:7 ] == "models/"
77
83
]
78
84
except Exception :
@@ -92,13 +98,13 @@ def pipe(
92
98
return "Error: GOOGLE_API_KEY is not set"
93
99
94
100
try :
95
- genai .configure (api_key = self .valves .GOOGLE_API_KEY )
101
+ client = genai .Client (api_key = self .valves .GOOGLE_API_KEY )
96
102
97
103
if model_id .startswith ("google_genai." ):
98
104
model_id = model_id [12 :]
99
105
model_id = model_id .lstrip ("." )
100
106
101
- if not model_id .startswith ("gemini-" ):
107
+ if not ( model_id .startswith ("gemini-" ) or model_id . startswith ( "learnlm-" ) or model_id . startswith ( "gemma-" ) ):
102
108
return f"Error: Invalid model name format: { model_id } "
103
109
104
110
print (f"Pipe function called for model: { model_id } " )
@@ -127,50 +133,78 @@ def pipe(
127
133
"role" : "user" if message ["role" ] == "user" else "model" ,
128
134
"parts" : [{"text" : message ["content" ]}]
129
135
})
130
-
131
- if "gemini-1.5" in model_id :
132
- model = genai .GenerativeModel (model_name = model_id , system_instruction = system_message )
133
- else :
134
- if system_message :
135
- contents .insert (0 , {"role" : "user" , "parts" : [{"text" : f"System: { system_message } " }]})
136
-
137
- model = genai .GenerativeModel (model_name = model_id )
138
-
139
- generation_config = GenerationConfig (
140
- temperature = body .get ("temperature" , 0.7 ),
141
- top_p = body .get ("top_p" , 0.9 ),
142
- top_k = body .get ("top_k" , 40 ),
143
- max_output_tokens = body .get ("max_tokens" , 8192 ),
144
- stop_sequences = body .get ("stop" , []),
145
- )
136
+ print (f"{ contents } " )
137
+
138
+ generation_config = {
139
+ "temperature" : body .get ("temperature" , 0.7 ),
140
+ "top_p" : body .get ("top_p" , 0.9 ),
141
+ "top_k" : body .get ("top_k" , 40 ),
142
+ "max_output_tokens" : body .get ("max_tokens" , 8192 ),
143
+ "stop_sequences" : body .get ("stop" , []),
144
+ "response_modalities" : ['Text' ]
145
+ }
146
+
147
+ if self .valves .GENERATE_IMAGE and model_id .startswith ("gemini-2.0-flash-exp" ):
148
+ generation_config ["response_modalities" ].append ("Image" )
146
149
147
150
if self .valves .USE_PERMISSIVE_SAFETY :
148
- safety_settings = {
149
- genai .types .HarmCategory .HARM_CATEGORY_HARASSMENT : genai .types .HarmBlockThreshold .BLOCK_NONE ,
150
- genai .types .HarmCategory .HARM_CATEGORY_HATE_SPEECH : genai .types .HarmBlockThreshold .BLOCK_NONE ,
151
- genai .types .HarmCategory .HARM_CATEGORY_SEXUALLY_EXPLICIT : genai .types .HarmBlockThreshold .BLOCK_NONE ,
152
- genai .types .HarmCategory .HARM_CATEGORY_DANGEROUS_CONTENT : genai .types .HarmBlockThreshold .BLOCK_NONE ,
153
- }
151
+ safety_settings = [
152
+ types .SafetySetting (category = 'HARM_CATEGORY_HARASSMENT' , threshold = 'OFF' ),
153
+ types .SafetySetting (category = 'HARM_CATEGORY_HATE_SPEECH' , threshold = 'OFF' ),
154
+ types .SafetySetting (category = 'HARM_CATEGORY_SEXUALLY_EXPLICIT' , threshold = 'OFF' ),
155
+ types .SafetySetting (category = 'HARM_CATEGORY_DANGEROUS_CONTENT' , threshold = 'OFF' ),
156
+ types .SafetySetting (category = 'HARM_CATEGORY_CIVIC_INTEGRITY' , threshold = 'OFF' )
157
+ ]
158
+ generation_config = types .GenerateContentConfig (** generation_config , safety_settings = safety_settings )
154
159
else :
155
- safety_settings = body . get ( "safety_settings" )
160
+ generation_config = types . GenerateContentConfig ( ** generation_config )
156
161
157
- response = model .generate_content (
158
- contents ,
159
- generation_config = generation_config ,
160
- safety_settings = safety_settings ,
161
- stream = body .get ("stream" , False ),
162
- )
162
+ if system_message :
163
+ contents .insert (0 , {"role" : "user" , "parts" : [{"text" : f"System: { system_message } " }]})
163
164
164
165
if body .get ("stream" , False ):
166
+ response = client .models .generate_content_stream (
167
+ model = model_id ,
168
+ contents = contents ,
169
+ config = generation_config ,
170
+ )
165
171
return self .stream_response (response )
166
172
else :
167
- return response .text
173
+ response = client .models .generate_content (
174
+ model = model_id ,
175
+ contents = contents ,
176
+ config = generation_config ,
177
+ )
178
+ for part in response .candidates [0 ].content .parts :
179
+ if part .text is not None :
180
+ return part .text
181
+ elif part .inline_data is not None :
182
+ try :
183
+ image_data = base64 .b64decode (part .inline_data .data )
184
+ image = Image .open (BytesIO ((image_data )))
185
+ content_type = part .inline_data .mime_type
186
+ return "Image not supported yet."
187
+ except Exception as e :
188
+ print (f"Error processing image: { e } " )
189
+ return "Error processing image."
168
190
169
191
except Exception as e :
170
192
print (f"Error generating content: { e } " )
171
- return f"An error occurred: { str ( e ) } "
193
+ return f"{ e } "
172
194
173
195
def stream_response (self , response ):
174
196
for chunk in response :
175
- if chunk .text :
176
- yield chunk .text
197
+ for candidate in chunk .candidates :
198
+ if candidate .content .parts is not None :
199
+ for part in candidate .content .parts :
200
+ if part .text is not None :
201
+ yield chunk .text
202
+ elif part .inline_data is not None :
203
+ try :
204
+ image_data = base64 .b64decode (part .inline_data .data )
205
+ image = Image .open (BytesIO (image_data ))
206
+ content_type = part .inline_data .mime_type
207
+ yield "Image not supported yet."
208
+ except Exception as e :
209
+ print (f"Error processing image: { e } " )
210
+ yield "Error processing image."
0 commit comments