Skip to content

Commit 2b5acb9

Browse files
douglas-reidcopybara-github
authored andcommitted
feat(models): add support for gemma model via gemini api
Merge #2857 Adds support for invoking Gemma models via the Gemini API endpoint. To support agentic function, callbacks are added which can extract and transform function calls and responses into user and model messages in the history. This change is intended to allow developers to explore the use of Gemma models for agentic purposes without requiring local deployment of the models. This should ease the burden of experimentation and testing for developers. A basic "hello world" style agent example is provided to demonstrate proper functioning of Gemma 3 models inside an Agent container, using the dice roll + prime check framework of similar examples for other models. ## Testing ### Testing Plan - add and run integration and unit tests - manual run of example `multi_tool_agent` from quickstart using new `Gemma` model - manual run of `hello_world_gemma` agent ### Automated Test Results: | Test Command | Results | |----------------|---------| | pytest ./tests/unittests | 4386 passed, 2849 warnings in 58.43s | | pytest ./tests/unittests/models/test_google_llm.py | 100 passed, 4 warnings in 5.83s | | pytest ./tests/integration/models/test_google_llm.py | 5 passed, 2 warnings in 3.73s | ### Manual Testing Here is a log of `multi_tool_agent` run with locally-built wheel and using Gemma model. ``` ❯ adk run multi_tool_agent Log setup complete: /var/folders/bg/_133c0ds2kb7cn699cpmmh_h0061bp/T/agents_log/agent.20250904_152617.log To access latest log: tail -F /var/folders/bg/_133c0ds2kb7cn699cpmmh_h0061bp/T/agents_log/agent.latest.log /Users/<redacted>/venvs/adk-quickstart/lib/python3.11/site-packages/google/adk/cli/cli.py:143: UserWarning: [EXPERIMENTAL] InMemoryCredentialService: This feature is experimental and may change or be removed in future versions without notice. It may introduce breaking changes at any time. credential_service = InMemoryCredentialService() /Users/<redacted>/venvs/adk-quickstart/lib/python3.11/site-packages/google/adk/auth/credential_service/in_memory_credential_service.py:33: UserWarning: [EXPERIMENTAL] BaseCredentialService: This feature is experimental and may change or be removed in future versions without notice. It may introduce breaking changes at any time. super().__init__() Running agent weather_time_agent, type exit to exit. [user]: what's the weather like today? [weather_time_agent]: Which city are you asking about? [user]: new york [weather_time_agent]: OK. The weather in New York is sunny with a temperature of 25 degrees Celsius (77 degrees Fahrenheit). ``` And here is a snippet of a log generated with DEBUG level logging of the `hello_world_gemma` sample. It demonstrates how function calls are extracted and inserted based on Gemma model interactions: ``` ... 2025-09-04 15:32:41,708 - DEBUG - google_llm.py:138 - LLM Request: ----------------------------------------------------------- System Instruction: None ----------------------------------------------------------- Contents: {"parts":[{"text":"\n You roll dice and answer questions about the outcome of the dice rolls.\n You can roll dice of different sizes...\n"}],"role":"user"} {"parts":[{"text":"Hi, introduce yourself."}],"role":"user"} {"parts":[{"text":"Hello! I am data_processing_agent, a hello world agent that can roll many-sided dice and check if numbers are prime. I'm ready to assist you with those tasks. Let's begin!\n\n\n\n"}],"role":"model"} {"parts":[{"text":"Roll a die with 100 sides and check if it is prime"}],"role":"user"} {"parts":[{"text":"{\"args\":{\"sides\":100},\"name\":\"roll_die\"}"}],"role":"model"} {"parts":[{"text":"Invoking tool `roll_die` produced: `{\"result\": 82}`."}],"role":"user"} {"parts":[{"text":"{\"args\":{\"nums\":[82]},\"name\":\"check_prime\"}"}],"role":"model"} {"parts":[{"text":"Invoking tool `check_prime` produced: `{\"result\": \"No prime numbers found.\"}`."}],"role":"user"} {"parts":[{"text":"The die roll was 82, and it is not a prime number.\n\n\n\n"}],"role":"model"} {"parts":[{"text":"Roll it again."}],"role":"user"} ----------------------------------------------------------- Functions: ----------------------------------------------------------- 2025-09-04 15:32:41,708 - INFO - models.py:8165 - AFC is enabled with max remote calls: 10. 2025-09-04 15:32:42,693 - INFO - google_llm.py:180 - Response received from the model. 2025-09-04 15:32:42,693 - DEBUG - google_llm.py:181 - LLM Response: ----------------------------------------------------------- Text: {"args":{"sides":100},"name":"roll_die"} ----------------------------------------------------------- ... ``` COPYBARA_INTEGRATE_REVIEW=#2857 from douglas-reid:add-gemma-via-api e6d015f PiperOrigin-RevId: 816451001
1 parent 84f2f41 commit 2b5acb9

File tree

8 files changed

+1087
-3
lines changed

8 files changed

+1087
-3
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Copyright 2025 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
16+
from . import agent
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# Copyright 2025 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
16+
import random
17+
18+
from google.adk.agents.llm_agent import Agent
19+
from google.adk.models.gemma_llm import Gemma
20+
from google.genai.types import GenerateContentConfig
21+
22+
23+
def roll_die(sides: int) -> int:
24+
"""Roll a die and return the rolled result.
25+
26+
Args:
27+
sides: The integer number of sides the die has.
28+
29+
Returns:
30+
An integer of the result of rolling the die.
31+
"""
32+
return random.randint(1, sides)
33+
34+
35+
async def check_prime(nums: list[int]) -> str:
36+
"""Check if a given list of numbers are prime.
37+
38+
Args:
39+
nums: The list of numbers to check.
40+
41+
Returns:
42+
A str indicating which number is prime.
43+
"""
44+
primes = set()
45+
for number in nums:
46+
number = number
47+
if number <= 1:
48+
continue
49+
is_prime = True
50+
for i in range(2, int(number**0.5) + 1):
51+
if number % i == 0:
52+
is_prime = False
53+
break
54+
if is_prime:
55+
primes.add(number)
56+
return (
57+
"No prime numbers found."
58+
if not primes
59+
else f"{', '.join(str(num) for num in primes)} are prime numbers."
60+
)
61+
62+
63+
root_agent = Agent(
64+
model=Gemma(model="gemma-3-27b-it"),
65+
name="data_processing_agent",
66+
description=(
67+
"hello world agent that can roll many-sided dice and check if numbers"
68+
" are prime."
69+
),
70+
instruction="""
71+
You roll dice and answer questions about the outcome of the dice rolls.
72+
You can roll dice of different sizes.
73+
You can use multiple tools in parallel by calling functions in parallel(in one request and in one round).
74+
It is ok to discuss previous dice roles, and comment on the dice rolls.
75+
When you are asked to roll a die, you must call the roll_die tool with the number of sides. Be sure to pass in an integer. Do not pass in a string.
76+
You should never roll a die on your own.
77+
When checking prime numbers, call the check_prime tool with a list of integers. Be sure to pass in a list of integers. You should never pass in a string.
78+
You should not check prime numbers before calling the tool.
79+
When you are asked to roll a die and check prime numbers, you should always make the following two function calls:
80+
1. You should first call the roll_die tool to get a roll. Wait for the function response before calling the check_prime tool.
81+
2. After the user reports a response from roll_die tool, you should call the check_prime tool with the roll_die result.
82+
2.1 If user asks you to check primes based on previous rolls, make sure you include the previous rolls in the list.
83+
3. When you respond, you must include the roll_die result from step 1.
84+
You should always perform the previous 3 steps when asking for a roll and checking prime numbers.
85+
You should not rely on the previous history on prime results.
86+
""",
87+
tools=[
88+
roll_die,
89+
check_prime,
90+
],
91+
generate_content_config=GenerateContentConfig(
92+
temperature=1.0,
93+
top_p=0.95,
94+
),
95+
)
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# Copyright 2025 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
16+
import asyncio
17+
import logging
18+
import time
19+
20+
import agent
21+
from dotenv import load_dotenv
22+
from google.adk.artifacts.in_memory_artifact_service import InMemoryArtifactService
23+
from google.adk.cli.utils import logs
24+
from google.adk.runners import Runner
25+
from google.adk.sessions.in_memory_session_service import InMemorySessionService
26+
from google.adk.sessions.session import Session
27+
from google.genai import types
28+
29+
load_dotenv(override=True)
30+
logs.log_to_tmp_folder(level=logging.INFO)
31+
32+
33+
async def main():
34+
app_name = 'my_gemma_app'
35+
user_id_1 = 'user1'
36+
session_service = InMemorySessionService()
37+
artifact_service = InMemoryArtifactService()
38+
runner = Runner(
39+
app_name=app_name,
40+
agent=agent.root_agent,
41+
artifact_service=artifact_service,
42+
session_service=session_service,
43+
)
44+
session_11 = await session_service.create_session(
45+
app_name=app_name, user_id=user_id_1
46+
)
47+
48+
async def run_prompt(session: Session, new_message: str):
49+
content = types.Content(
50+
role='user', parts=[types.Part.from_text(text=new_message)]
51+
)
52+
print('** User says:', content.model_dump(exclude_none=True))
53+
async for event in runner.run_async(
54+
user_id=user_id_1,
55+
session_id=session.id,
56+
new_message=content,
57+
):
58+
if event.content.parts and event.content.parts[0].text:
59+
print(f'** {event.author}: {event.content.parts[0].text}')
60+
61+
start_time = time.time()
62+
print('Start time:', start_time)
63+
print('------------------------------------')
64+
await run_prompt(session_11, 'Hi, introduce yourself.')
65+
await run_prompt(
66+
session_11, 'Roll a die with 100 sides and check if it is prime'
67+
)
68+
await run_prompt(session_11, 'Roll it again.')
69+
await run_prompt(session_11, 'What numbers did I get?')
70+
end_time = time.time()
71+
print('------------------------------------')
72+
print('End time:', end_time)
73+
print('Total time:', end_time - start_time)
74+
75+
76+
if __name__ == '__main__':
77+
asyncio.run(main())

src/google/adk/models/__init__.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"""Defines the interface to support a model."""
1616

1717
from .base_llm import BaseLlm
18+
from .gemma_llm import Gemma
1819
from .google_llm import Gemini
1920
from .llm_request import LlmRequest
2021
from .llm_response import LlmResponse
@@ -23,9 +24,10 @@
2324
__all__ = [
2425
'BaseLlm',
2526
'Gemini',
27+
'Gemma',
2628
'LLMRegistry',
2729
]
2830

2931

30-
for regex in Gemini.supported_models():
31-
LLMRegistry.register(Gemini)
32+
LLMRegistry.register(Gemini)
33+
LLMRegistry.register(Gemma)

0 commit comments

Comments
 (0)