livekit-plugins-google 1.3.8__py3-none-any.whl → 1.3.11__py3-none-any.whl

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.
@@ -1,11 +1,71 @@
1
- from typing import Union
1
+ from abc import ABC, abstractmethod
2
+ from dataclasses import dataclass
3
+ from typing import Optional
2
4
 
3
- from google.genai.types import (
4
- GoogleMaps,
5
- GoogleSearch,
6
- GoogleSearchRetrieval,
7
- ToolCodeExecution,
8
- UrlContext,
9
- )
5
+ from google.genai import types
6
+ from livekit.agents import llm
10
7
 
11
- _LLMTool = Union[GoogleSearchRetrieval, ToolCodeExecution, GoogleSearch, UrlContext, GoogleMaps]
8
+
9
+ class GeminiTool(llm.ProviderTool, ABC):
10
+ @abstractmethod
11
+ def to_tool_config(self) -> types.Tool: ...
12
+
13
+
14
+ @dataclass
15
+ class GoogleSearch(GeminiTool):
16
+ exclude_domains: Optional[list[str]] = None
17
+ blocking_confidence: Optional[types.PhishBlockThreshold] = None
18
+ time_range_filter: Optional[types.Interval] = None
19
+
20
+ def to_tool_config(self) -> types.Tool:
21
+ return types.Tool(
22
+ google_search=types.GoogleSearch(
23
+ exclude_domains=self.exclude_domains,
24
+ blocking_confidence=self.blocking_confidence,
25
+ time_range_filter=self.time_range_filter,
26
+ )
27
+ )
28
+
29
+
30
+ @dataclass
31
+ class GoogleMaps(GeminiTool):
32
+ auth_config: Optional[types.AuthConfig] = None
33
+ enable_widget: Optional[bool] = None
34
+
35
+ def to_tool_config(self) -> types.Tool:
36
+ return types.Tool(
37
+ google_maps=types.GoogleMaps(
38
+ auth_config=self.auth_config,
39
+ enable_widget=self.enable_widget,
40
+ )
41
+ )
42
+
43
+
44
+ class URLContext(GeminiTool):
45
+ def to_tool_config(self) -> types.Tool:
46
+ return types.Tool(
47
+ url_context=types.UrlContext(),
48
+ )
49
+
50
+
51
+ @dataclass
52
+ class FileSearch(GeminiTool):
53
+ file_search_store_names: list[str]
54
+ top_k: Optional[int] = None
55
+ metadata_filter: Optional[str] = None
56
+
57
+ def to_tool_config(self) -> types.Tool:
58
+ return types.Tool(
59
+ file_search=types.FileSearch(
60
+ file_search_store_names=self.file_search_store_names,
61
+ top_k=self.top_k,
62
+ metadata_filter=self.metadata_filter,
63
+ )
64
+ )
65
+
66
+
67
+ class ToolCodeExecution(GeminiTool):
68
+ def to_tool_config(self) -> types.Tool:
69
+ return types.Tool(
70
+ code_execution=types.ToolCodeExecution(),
71
+ )
@@ -32,10 +32,9 @@ from livekit.agents.types import DEFAULT_API_CONNECT_OPTIONS, NOT_GIVEN, NotGive
32
32
  from livekit.agents.utils import is_given
33
33
 
34
34
  from .log import logger
35
- from .models import Gender, SpeechLanguages
35
+ from .models import GeminiTTSModels, Gender, SpeechLanguages
36
36
 
37
37
  NUM_CHANNELS = 1
38
- DEFAULT_VOICE_NAME = "en-US-Chirp3-HD-Charon"
39
38
  DEFAULT_LANGUAGE = "en-US"
40
39
  DEFAULT_GENDER = "neutral"
41
40
 
@@ -65,7 +64,7 @@ class TTS(tts.TTS):
65
64
  gender: NotGivenOr[Gender | str] = NOT_GIVEN,
66
65
  voice_name: NotGivenOr[str] = NOT_GIVEN,
67
66
  voice_cloning_key: NotGivenOr[str] = NOT_GIVEN,
68
- model_name: NotGivenOr[str] = NOT_GIVEN,
67
+ model_name: GeminiTTSModels | str = "gemini-2.5-flash-tts",
69
68
  prompt: NotGivenOr[str] = NOT_GIVEN,
70
69
  sample_rate: int = 24000,
71
70
  pitch: int = 0,
@@ -73,7 +72,7 @@ class TTS(tts.TTS):
73
72
  speaking_rate: float = 1.0,
74
73
  volume_gain_db: float = 0.0,
75
74
  location: str = "global",
76
- audio_encoding: texttospeech.AudioEncoding = texttospeech.AudioEncoding.OGG_OPUS, # type: ignore
75
+ audio_encoding: texttospeech.AudioEncoding = texttospeech.AudioEncoding.PCM, # type: ignore
77
76
  credentials_info: NotGivenOr[dict] = NOT_GIVEN,
78
77
  credentials_file: NotGivenOr[str] = NOT_GIVEN,
79
78
  tokenizer: NotGivenOr[tokenize.SentenceTokenizer] = NOT_GIVEN,
@@ -92,9 +91,9 @@ class TTS(tts.TTS):
92
91
  Args:
93
92
  language (SpeechLanguages | str, optional): Language code (e.g., "en-US"). Default is "en-US".
94
93
  gender (Gender | str, optional): Voice gender ("male", "female", "neutral"). Default is "neutral".
95
- voice_name (str, optional): Specific voice name. Default is an empty string.
94
+ voice_name (str, optional): Specific voice name. Default is an empty string. See https://docs.cloud.google.com/text-to-speech/docs/gemini-tts#voice_options for supported voice in Gemini TTS models.
96
95
  voice_cloning_key (str, optional): Voice clone key. Created via https://cloud.google.com/text-to-speech/docs/chirp3-instant-custom-voice
97
- model_name (str, optional): Model name for TTS (e.g., "gemini-2.5-flash-tts"). Enables Gemini TTS models with streaming support.
96
+ model_name (GeminiTTSModels | str, optional): Model name for TTS (e.g., "gemini-2.5-flash-tts", "chirp_3"). Default is "gemini-2.5-flash-tts".
98
97
  prompt (str, optional): Style prompt for Gemini TTS models. Controls tone, style, and speaking characteristics. Only applied to first input chunk in streaming mode.
99
98
  sample_rate (int, optional): Audio sample rate in Hz. Default is 24000.
100
99
  location (str, optional): Location for the TTS client. Default is "global".
@@ -134,14 +133,20 @@ class TTS(tts.TTS):
134
133
  language_code=lang,
135
134
  ssml_gender=ssml_gender,
136
135
  )
137
- if is_given(model_name):
136
+ if model_name != "chirp_3": # voice_params.model_name must not be set for Chirp 3
138
137
  voice_params.model_name = model_name
138
+
139
139
  if is_given(voice_cloning_key):
140
140
  voice_params.voice_clone = texttospeech.VoiceCloneParams(
141
141
  voice_cloning_key=voice_cloning_key,
142
142
  )
143
143
  else:
144
- voice_params.name = voice_name if is_given(voice_name) else DEFAULT_VOICE_NAME
144
+ if is_given(voice_name):
145
+ voice_params.name = voice_name
146
+ elif model_name == "chirp_3":
147
+ voice_params.name = "en-US-Chirp3-HD-Charon"
148
+ else:
149
+ voice_params.name = "Charon"
145
150
 
146
151
  if not is_given(tokenizer):
147
152
  tokenizer = tokenize.blingfire.SentenceTokenizer()
@@ -160,7 +165,7 @@ class TTS(tts.TTS):
160
165
  custom_pronunciations=pronunciations,
161
166
  enable_ssml=enable_ssml,
162
167
  use_markup=use_markup,
163
- model_name=model_name if is_given(model_name) else None,
168
+ model_name=model_name,
164
169
  prompt=prompt if is_given(prompt) else None,
165
170
  )
166
171
  self._streams = weakref.WeakSet[SynthesizeStream]()
@@ -285,6 +290,9 @@ class ChunkedStream(tts.ChunkedStream):
285
290
  text=self._input_text, custom_pronunciations=self._opts.custom_pronunciations
286
291
  )
287
292
 
293
+ if self._opts.prompt is not None:
294
+ tts_input.prompt = self._opts.prompt
295
+
288
296
  response: SynthesizeSpeechResponse = await self._tts._ensure_client().synthesize_speech(
289
297
  input=tts_input,
290
298
  voice=self._opts.voice,
@@ -9,90 +9,40 @@ from pydantic import TypeAdapter
9
9
  from google.genai import types
10
10
  from livekit.agents import llm
11
11
  from livekit.agents.llm import utils as llm_utils
12
- from livekit.agents.llm.tool_context import (
13
- FunctionTool,
14
- RawFunctionTool,
15
- get_raw_function_info,
16
- is_function_tool,
17
- is_raw_function_tool,
18
- )
19
12
  from livekit.agents.types import NOT_GIVEN, NotGivenOr
20
13
  from livekit.agents.utils import is_given
21
14
 
22
- from .log import logger
23
- from .tools import _LLMTool
15
+ from .tools import GeminiTool
24
16
 
25
- __all__ = ["to_fnc_ctx"]
26
-
27
-
28
- def to_fnc_ctx(
29
- fncs: list[FunctionTool | RawFunctionTool],
30
- *,
31
- use_parameters_json_schema: bool = True,
32
- tool_behavior: NotGivenOr[types.Behavior] = NOT_GIVEN,
33
- ) -> list[types.FunctionDeclaration]:
34
- tools: list[types.FunctionDeclaration] = []
35
- for fnc in fncs:
36
- if is_raw_function_tool(fnc):
37
- info = get_raw_function_info(fnc)
38
- fnc_kwargs = {
39
- "name": info.name,
40
- "description": info.raw_schema.get("description", ""),
41
- }
42
- if use_parameters_json_schema:
43
- fnc_kwargs["parameters_json_schema"] = info.raw_schema.get("parameters", {})
44
- else:
45
- # https://github.com/googleapis/python-genai/issues/1147
46
- fnc_kwargs["parameters"] = types.Schema.from_json_schema(
47
- json_schema=types.JSONSchema.model_validate(
48
- info.raw_schema.get("parameters", {})
49
- )
50
- )
51
-
52
- if is_given(tool_behavior):
53
- fnc_kwargs["behavior"] = tool_behavior
54
- tools.append(types.FunctionDeclaration(**fnc_kwargs))
55
-
56
- elif is_function_tool(fnc):
57
- tools.append(_build_gemini_fnc(fnc, tool_behavior=tool_behavior))
58
-
59
- return tools
17
+ __all__ = ["create_tools_config"]
60
18
 
61
19
 
62
20
  def create_tools_config(
21
+ tool_ctx: llm.ToolContext,
63
22
  *,
64
- function_tools: list[types.FunctionDeclaration] | None = None,
65
- gemini_tools: list[_LLMTool] | None = None,
23
+ tool_behavior: NotGivenOr[types.Behavior] = NOT_GIVEN,
24
+ _only_single_type: bool = False,
66
25
  ) -> list[types.Tool]:
67
- tools: list[types.Tool] = []
26
+ gemini_tools: list[types.Tool] = []
68
27
 
28
+ function_tools = [
29
+ types.FunctionDeclaration.model_validate(schema)
30
+ for schema in tool_ctx.parse_function_tools(
31
+ "google", tool_behavior=tool_behavior.value if tool_behavior else None
32
+ )
33
+ ]
69
34
  if function_tools:
70
- tools.append(types.Tool(function_declarations=function_tools))
71
-
72
- if gemini_tools:
73
- for tool in gemini_tools:
74
- if isinstance(tool, types.GoogleSearchRetrieval):
75
- tools.append(types.Tool(google_search_retrieval=tool))
76
- elif isinstance(tool, types.ToolCodeExecution):
77
- tools.append(types.Tool(code_execution=tool))
78
- elif isinstance(tool, types.GoogleSearch):
79
- tools.append(types.Tool(google_search=tool))
80
- elif isinstance(tool, types.UrlContext):
81
- tools.append(types.Tool(url_context=tool))
82
- elif isinstance(tool, types.GoogleMaps):
83
- tools.append(types.Tool(google_maps=tool))
84
- else:
85
- logger.warning(f"Warning: Received unhandled tool type: {type(tool)}")
86
- continue
35
+ gemini_tools.append(types.Tool(function_declarations=function_tools))
87
36
 
88
- if len(tools) > 1:
89
- # https://github.com/google/adk-python/issues/53#issuecomment-2799538041
90
- logger.warning(
91
- "Multiple kinds of tools are not supported in Gemini. Only the first tool will be used."
92
- )
93
- tools = tools[:1]
37
+ # Some Google LLMs do not support multiple tool types (either function tools or builtin tools).
38
+ if _only_single_type and gemini_tools:
39
+ return gemini_tools
40
+
41
+ for tool in tool_ctx.provider_tools:
42
+ if isinstance(tool, GeminiTool):
43
+ gemini_tools.append(tool.to_tool_config())
94
44
 
95
- return tools
45
+ return gemini_tools
96
46
 
97
47
 
98
48
  def get_tool_results_for_realtime(
@@ -124,22 +74,6 @@ def get_tool_results_for_realtime(
124
74
  )
125
75
 
126
76
 
127
- def _build_gemini_fnc(
128
- function_tool: FunctionTool, *, tool_behavior: NotGivenOr[types.Behavior] = NOT_GIVEN
129
- ) -> types.FunctionDeclaration:
130
- fnc = llm.utils.build_legacy_openai_schema(function_tool, internally_tagged=True)
131
- json_schema = _GeminiJsonSchema(fnc["parameters"]).simplify()
132
-
133
- kwargs = {
134
- "name": fnc["name"],
135
- "description": fnc["description"],
136
- "parameters": types.Schema.model_validate(json_schema) if json_schema else None,
137
- }
138
- if is_given(tool_behavior):
139
- kwargs["behavior"] = tool_behavior
140
- return types.FunctionDeclaration(**kwargs)
141
-
142
-
143
77
  def to_response_format(response_format: type | dict) -> types.SchemaUnion:
144
78
  _, json_schema_type = llm_utils.to_response_format_param(response_format)
145
79
  if isinstance(json_schema_type, TypeAdapter):
@@ -12,4 +12,4 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- __version__ = "1.3.8"
15
+ __version__ = "1.3.11"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: livekit-plugins-google
3
- Version: 1.3.8
3
+ Version: 1.3.11
4
4
  Summary: Agent Framework plugin for services from Google Cloud
5
5
  Project-URL: Documentation, https://docs.livekit.io
6
6
  Project-URL: Website, https://livekit.io/
@@ -0,0 +1,18 @@
1
+ livekit/plugins/google/__init__.py,sha256=21ZYfsz4d4a5tP_po9WPtIv552gYh6thg7mcnkYA9vc,1445
2
+ livekit/plugins/google/llm.py,sha256=eLLuXQZ0GMrl8blfb2staHr2ClAsYroJRydt0arT1Uk,24386
3
+ livekit/plugins/google/log.py,sha256=GI3YWN5YzrafnUccljzPRS_ZALkMNk1i21IRnTl2vNA,69
4
+ livekit/plugins/google/models.py,sha256=NOkEJVTmvZ7A6TSVCACoaST-qM84YSPwo_HkM5ct1mY,3171
5
+ livekit/plugins/google/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ livekit/plugins/google/stt.py,sha256=Q3-gEAV5xykTLfmkj8bPq6mHdx4cn6W7_-fQrI7n85o,35246
7
+ livekit/plugins/google/tools.py,sha256=cH8qXkQj6zQ_cWUV-4apjSeQaMhfK6907XKPAs9PVNo,1956
8
+ livekit/plugins/google/tts.py,sha256=dVhGDN0Q-b_tGKdridkb5grl8YhBMcxTc_kIdcg4gQ8,19251
9
+ livekit/plugins/google/utils.py,sha256=MSMsmPBsmxL-rSi5mc8d7ViI5bHgNEOO4Em0yHLhNyQ,8128
10
+ livekit/plugins/google/version.py,sha256=Db2pVVYNC02fj0G6tMcFzTjBbAxJWVcZ5l1gTIq9VU4,601
11
+ livekit/plugins/google/beta/__init__.py,sha256=4q5dx-Y6o9peCDziB03Skf5ngH4PTBsZC86ZawWrgnk,271
12
+ livekit/plugins/google/beta/gemini_tts.py,sha256=SpKorOteQ7GYoGWsxV5YPuGeMexoosmtDXQVz_1ZeLA,8743
13
+ livekit/plugins/google/realtime/__init__.py,sha256=_fW2NMN22F-hnQ4xAJ_g5lPbR7CvM_xXzSWlUQY-E-U,188
14
+ livekit/plugins/google/realtime/api_proto.py,sha256=n6Rb-3qrZyByp8MSkBHA3TIW2E0IGKH1Xj7hKJM029M,2290
15
+ livekit/plugins/google/realtime/realtime_api.py,sha256=_eCRUK2Bi4ypBEPcCzK9ym_isO8BAvmlXkgW1cO4P8w,54723
16
+ livekit_plugins_google-1.3.11.dist-info/METADATA,sha256=sQqXHcC_xM0n6_MNZYlJJq61WRG4y422eYFDy0YeXD4,2467
17
+ livekit_plugins_google-1.3.11.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
18
+ livekit_plugins_google-1.3.11.dist-info/RECORD,,
@@ -1,18 +0,0 @@
1
- livekit/plugins/google/__init__.py,sha256=bYHN04-Ttynj09POAnFP3mln-wrEc1vanUD_YpoWOE4,1434
2
- livekit/plugins/google/llm.py,sha256=OSl-3DzYRGPUvmL4go-iCc_iag9woyvn5ZTpElLAnd0,21396
3
- livekit/plugins/google/log.py,sha256=GI3YWN5YzrafnUccljzPRS_ZALkMNk1i21IRnTl2vNA,69
4
- livekit/plugins/google/models.py,sha256=jsXHLSCDw-T5dZXeDE2nMT2lr0GooCYO4y4aW7Htps4,2816
5
- livekit/plugins/google/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- livekit/plugins/google/stt.py,sha256=fsWoNnpjgLxqY43cx6GbRI-_QLvXmMvD4WczJFjnoOA,26846
7
- livekit/plugins/google/tools.py,sha256=tD5HVDHO5JfUF029Cx3axHMJec0Gxalkl7s1FDgxLzI,259
8
- livekit/plugins/google/tts.py,sha256=6qQDLRawqW5SSoN4JAqZYjyANxKT3165zJDfO6_7f08,18846
9
- livekit/plugins/google/utils.py,sha256=U_h7kV3528uH-Ef946yaMjS-Y9rH_yreu32fQ6qwVNo,10657
10
- livekit/plugins/google/version.py,sha256=QU6KNV-p6CdJv8bUwDgVMAemqOx2EwSsOFOuYh4-Re0,600
11
- livekit/plugins/google/beta/__init__.py,sha256=4q5dx-Y6o9peCDziB03Skf5ngH4PTBsZC86ZawWrgnk,271
12
- livekit/plugins/google/beta/gemini_tts.py,sha256=SpKorOteQ7GYoGWsxV5YPuGeMexoosmtDXQVz_1ZeLA,8743
13
- livekit/plugins/google/realtime/__init__.py,sha256=_fW2NMN22F-hnQ4xAJ_g5lPbR7CvM_xXzSWlUQY-E-U,188
14
- livekit/plugins/google/realtime/api_proto.py,sha256=6zZ5foiBkHfq6p9-fPf-fzBACAKFj5G7tJ_2PWPpoLg,1432
15
- livekit/plugins/google/realtime/realtime_api.py,sha256=SMEJ6ENBwVBt5DapcIaTiwzY9ANbeMDv_5tjifiQ4Ec,55231
16
- livekit_plugins_google-1.3.8.dist-info/METADATA,sha256=HMmh2kVB5WDIAbGostE7cUIWJ-6-xE9tY6ipJrJ2rHo,2466
17
- livekit_plugins_google-1.3.8.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
18
- livekit_plugins_google-1.3.8.dist-info/RECORD,,