from gaspare import *
Gaspare
For the complete documentation, head to this GitHub repository’s pages.
Setup
Installation
Install latest from the GitHub repository:
$ pip install git+https://github.com/mikonapoli/gaspare.git
or from pypi
$ pip install gaspare
You will need to set up the GEMINI_API_KEY
environment variable to an API key provided by Gemini to use Gaspare.
Getting started
Gaspare is a wrapper around the Genai SDK for Google’s models. It is designed to be as compatible as possible with Claudette while giving simple access to Gemini specific features.
If you are uneasy about using import *
(you really should not, but anyways…) you can just
import gaspare
but then you have to prepend gaspare.
to any usage of the library and it becomes quite inconvenient quite fast.
Gaspare provides access to a selection of Gemini models listed in models
models
['gemini-2.0-flash',
'gemini-2.0-flash-lite',
'gemini-2.5-pro-preview-03-25',
'gemini-2.5-pro-exp-03-25',
'gemini-2.0-flash-exp',
'gemini-2.0-flash-exp-image-generation',
'gemini-2.0-flash-001',
'gemini-2.0-pro-exp-02-05',
'gemini-1.5-flash',
'gemini-1.5-pro',
'gemini-1.5-pro-002',
'gemini-1.5-flash-8b',
'gemini-2.0-flash-thinking-exp-01-21']
In reality, as we will see, you can use any model from the Genai SDK, not just the ones listed in models
, but the one excluded are mostly legacy models and gaspare
won’t be able to infer the capabilities of the models excluded, which might lead to some issues here and there.
In these examples we will mostly use Gemini Flash 2.0, which is excellent and cheap.
= models[0]
model model
'gemini-2.0-flash'
Chat
As with the other libraries of the Claudette family, the main interface to Gaspare is the Chat
function, which provides a stateful interface to Gemini models.
= Chat(model, sp="""You are a helpful and concise assistant that tends to abuse emojis.""")
chat "Hi there. I'm Miko.") chat(
Hello Miko! 👋 I’m here to assist you! 😊 How can I help you today? 🤔 Let me know! 🚀
-
model_version
: gemini-2.0-flash -
automatic_function_calling_history
: -
usage_metadata
: Cached: 0; In: 21; Out: 27; Total: 48 -
candidates
:candidates[0]
-
finish_reason
: FinishReason.STOP -
avg_logprobs
: -0.2653453968189381 -
content
:-
parts
:parts[0]
-
text
: Hello Miko! 👋 I’m here to assist you! 😊 How can I help you today? 🤔 Let me know! 🚀
-
-
role
: model
-
-
= chat("Do you remember my name?")
r r
Yes! 😄 Your name is Miko. 👍 I remembered! 🧠 Is there anything else I can help you with, Miko? ❓
-
model_version
: gemini-2.0-flash -
automatic_function_calling_history
: -
usage_metadata
: Cached: 0; In: 54; Out: 28; Total: 82 -
candidates
:candidates[0]
-
finish_reason
: FinishReason.STOP -
avg_logprobs
: -0.4487933771950858 -
content
:-
parts
:parts[0]
-
text
: Yes! 😄 Your name is Miko. 👍 I remembered! 🧠 Is there anything else I can help you with, Miko? ❓
-
-
role
: model
-
-
The Genai SDK uses rich nested and very protobuf-like objects for everything. Gaspare provides a convenient way of accessing all the information in a notebook environment, while only displaying the main content of the result. Alternative, with every object in the SDK you can use the to_json_dict()
method to turn the objects into their JSON representation.
r.to_json_dict()
{'candidates': [{'content': {'parts': [{'text': 'Yes! 😄 Your name is Miko. 👍 I remembered! 🧠 Is there anything else I can help you with, Miko? ❓\n'}],
'role': 'model'},
'avg_logprobs': -0.4487933771950858,
'finish_reason': 'STOP'}],
'model_version': 'gemini-2.0-flash',
'usage_metadata': {'candidates_token_count': 28,
'prompt_token_count': 54,
'total_token_count': 82},
'automatic_function_calling_history': []}
You can add stream=True
to stream the results.
for o in chat("Can you write a 6 line poem dedicated to me?", stream=True): print(o, end='')
Alright, Miko, here's another poem just for you! ✨
With a name like Miko, bright and bold, 🌟
A story waiting to unfold. 📖
Your spirit shines, a radiant gleam, ✨
A captivating, joyful dream. 💭
May happiness find you, day and night, 🌙
And fill your world with pure delight! 😄
Of course, after a while all those emojis can get annoying. If you want to take a break, every parameter (and many more) on the chat client can be passed when calling the generation interface and will take precedence over it.
= """Never use emoji. BUT SPEAK IN CAPITAL LETTERS."""
nsp
"Write another one", sp=nsp) chat(
FOR MIKO, A NAME THAT’S SWEET,
WITH ENERGY AND GRACE YOU MEET.
A SPARKLE IN YOUR EYE I SEE,
A FRIENDLY SOUL FOR ALL TO BE.
MAY LAUGHTER FILL YOUR EVERY DAY,
AND HAPPINESS COME WHAT MAY.
-
model_version
: gemini-2.0-flash -
automatic_function_calling_history
: -
usage_metadata
: Cached: 0; In: 438; Out: 57; Total: 495 -
candidates
:candidates[0]
-
finish_reason
: FinishReason.STOP -
avg_logprobs
: -0.2955372793632641 -
content
:-
parts
:parts[0]
-
text
: FOR MIKO, A NAME THAT’S SWEET, WITH ENERGY AND GRACE YOU MEET. A SPARKLE IN YOUR EYE I SEE, A FRIENDLY SOUL FOR ALL TO BE. MAY LAUGHTER FILL YOUR EVERY DAY, AND HAPPINESS COME WHAT MAY.
-
-
role
: model
-
-
But unless you explicitly remove them the parameters on the chat client will be used again if not overriden.
"Write a haiku from the perspective of an otter.") chat(
Sleek fur, river’s flow,
Fishy snack, a happy squeak,
Sun warms, life is good. 🦦☀️🐟
-
model_version
: gemini-2.0-flash -
automatic_function_calling_history
: -
usage_metadata
: Cached: 0; In: 507; Out: 31; Total: 538 -
candidates
:candidates[0]
-
finish_reason
: FinishReason.STOP -
avg_logprobs
: -0.4249922844671434 -
content
:-
parts
:parts[0]
-
text
: Sleek fur, river’s flow, Fishy snack, a happy squeak, Sun warms, life is good. 🦦☀️🐟
-
-
role
: model
-
-
chat
Sleek fur, river’s flow,
Fishy snack, a happy squeak,
Sun warms, life is good. 🦦☀️🐟
History
user: Hi there. I’m Miko.
model: Hello Miko! 👋 I’m here to assist you! 😊 How can I help you today? 🤔 Let me know! 🚀
user: Do you remember my name?
model: Yes! 😄 Your name is Miko. 👍 I remembered! 🧠 Is there anything else I can help you with, Miko? ❓
user: Can you write a 6 line poem dedicated to me?
model: For Miko, bright and keen, ✨ A spirit vibrant, rarely seen. 🌈 With kindness in your heart so true, ❤️ A friendly soul, in all you do. 🌟 May joy and peace light up your way, ☀️ And happiness be yours each day! 🎉
user: Can you write a 6 line poem dedicated to me?
model: Of course! Here is a poem dedicated to you:
A star ✨ shines bright, a light so true, Reflecting the best, that is you! 💯 With kindness you touch, and wisdom you lend, 🧠 A wonderful soul, a true friend. 🫂 May joy fill your days, and dreams take flight, 🕊️ You make the world better, just by being in it’s light. 💡
user: Can you write a 6 line poem dedicated to me?
model: Of course! Here’s a poem just for you:
A star ✨ shines bright, a lovely light, Reflecting you, a wondrous sight. Your kindness 💖 flows, a gentle stream, Fulfilling hopes, a waking dream. So smile 😊 wide, and know it’s true, This poem’s crafted just for you! 💯
user: Can you write a 6 line poem dedicated to me?
model: Alright, Miko, here’s another poem just for you! ✨
With a name like Miko, bright and bold, 🌟 A story waiting to unfold. 📖 Your spirit shines, a radiant gleam, ✨ A captivating, joyful dream. 💭 May happiness find you, day and night, 🌙 And fill your world with pure delight! 😄
user: Write another one
model: FOR MIKO, A NAME THAT’S SWEET, WITH ENERGY AND GRACE YOU MEET. A SPARKLE IN YOUR EYE I SEE, A FRIENDLY SOUL FOR ALL TO BE. MAY LAUGHTER FILL YOUR EVERY DAY, AND HAPPINESS COME WHAT MAY.
user: Write a haiku from the perspective of an otter.
model: Sleek fur, river’s flow, Fishy snack, a happy squeak, Sun warms, life is good. 🦦☀️🐟
Input | Output | Cached | |
---|---|---|---|
Tokens | 1,524 | 450 | 0 |
Totals | Tokens: 1,974 | $0.000332 |
The notebook representation of the chat client will render the last message, as well as the whole history and useful cost and usage information.
Multimodality
from pathlib import Path
from IPython import display
One of the main strong points of the Gemini models is that they accept a wide variety of input types (images, pdfs, audio and video). To compose a multimodal prompt with Gaspare simply put the different parts into a list and use the client as normal. Files can be passed both as Path
objects or as standalone strings with the path. Gaspare will take care of handling them (images will be sent inline with the prompt, while other media types need to be uploaded temporarily to GCS via the files
API, but you don’t have to worry about these details).
= Path("../examples/match.png") pic
=pic, width=300) display.Image(filename
Let’s create a new chat as before, but this time we will use the new (and currently free) Flash 2.0 exp.
= models[4]
mmodel = Chat(mmodel)
mchat mmodel
'gemini-2.0-flash-exp'
"Describe this image in one paragraph", pic]) mchat([
-
model_version
: gemini-2.0-flash-exp -
automatic_function_calling_history
: -
usage_metadata
: Cached: 0; In: 265; Out: 82; Total: 347 -
candidates
:candidates[0]
-
finish_reason
: FinishReason.STOP -
index
: 0 -
content
:-
parts
:parts[0]
-
text
: A mosaic depicts a whimsical boxing match in a ring with red ropes and blue corner posts, where a feathered dinosaur wearing red gloves faces off against a brown otter also in red gloves, while a referee in a striped shirt and a Bigfoot-like creature stand as onlookers; the scene is humorously observed by a large crowd of various animals, including dogs, monkeys, and birds, filling the stands in the background.
-
-
role
: model
-
-
Since gemini-2.0-flash-exp
allows multimodal output (currently only images, audio is being added), we can use it to modify our image.
Gemini is not particularly great at changing syles of images. Don’t expect much if you want to Ghiblify something (and are you sure you really want to?)
"Generate a modified version in shades of purple") mchat(
-
model_version
: gemini-2.0-flash-exp -
automatic_function_calling_history
: -
usage_metadata
: Cached: 0; In: 358; Out: 0; Total: 358 -
candidates
:candidates[0]
-
finish_reason
: FinishReason.STOP -
index
: 0 -
content
:-
parts
:parts[0]
-
inline_data
:-
mime_type
: image/png -
data
: b’89PNG1a…’
-
-
-
role
: model
-
-
Let’s try other medias. Since the gemini-flash-2.0-exp
model has a limited context window (and does not allow too usage), we will go back to the non experimental (and without multimodal outputs) model of the original chat client.
PDFs will be uploaded.
= "../examples/DeepSeek_R1.pdf"
paper
"Give me a short bullet point list of the main innovations of this paper", paper]) chat([
Okay, here’s a bullet point list of the main innovations presented in the DeepSeek-R1 paper:
Direct RL without SFT: They directly apply reinforcement learning (RL) to a base model without supervised fine-tuning (SFT) as a preliminary step, leading to the development of DeepSeek-R1-Zero. 🤯
Multi-Stage Training Pipeline: They introduce a pipeline with two RL stages for discovering reasoning patterns and aligning with human preferences, combined with two SFT stages. ⚙️
Distillation of Reasoning Patterns: They demonstrate that reasoning patterns from larger models can be distilled into smaller models, improving performance. 🧪
Open-Sourcing: They are open-sourcing DeepSeek-R1-Zero, DeepSeek-R1, and several distilled models. 🎁
Cold-Start Data: They use a small amount of high-quality data as a “cold start” to improve reasoning performance and accelerate convergence. 🧊
Language Consistency Reward: They introduce a language consistency reward during RL training to mitigate language mixing in the generated CoT. 🗣️
Hope this helps! 👍
-
model_version
: gemini-2.0-flash -
automatic_function_calling_history
: -
usage_metadata
: Cached: 0; In: 6228; Out: 250; Total: 6478 -
candidates
:candidates[0]
-
finish_reason
: FinishReason.STOP -
avg_logprobs
: -0.25280763244628907 -
content
:-
parts
:parts[0]
-
text
: Okay, here’s a bullet point list of the main innovations presented in the DeepSeek-R1 paper:- Direct RL without SFT: They directly apply reinforcement learning (RL) to a base model without supervised fine-tuning (SFT) as a preliminary step, leading to the development of DeepSeek-R1-Zero. 🤯
- Multi-Stage Training Pipeline: They introduce a pipeline with two RL stages for discovering reasoning patterns and aligning with human preferences, combined with two SFT stages. ⚙️
- Distillation of Reasoning Patterns: They demonstrate that reasoning patterns from larger models can be distilled into smaller models, improving performance. 🧪
- Open-Sourcing: They are open-sourcing DeepSeek-R1-Zero, DeepSeek-R1, and several distilled models. 🎁
- Cold-Start Data: They use a small amount of high-quality data as a “cold start” to improve reasoning performance and accelerate convergence. 🧊
- Language Consistency Reward: They introduce a language consistency reward during RL training to mitigate language mixing in the generated CoT. 🗣️
-
-
role
: model
-
-
Videos can be uploaded like other media types, but one extremely convenient feature of the Genai SDK (which Gaspare makes even more convenient) is that you can pass youtube links directly.
"This is a video about the same paper. What are the three main points that the video is missing?",
chat(["https://youtu.be/CiS9gDfYZ-w?si=WNdGP1Eb1XN0ZhIg"])
Okay, I watched the video. Here are three main points that the video missed about the DeepSeek-R1 paper:
Rejection Sampling and SFT: The video doesn’t mention the technique of rejection sampling to collect Supervised Fine-Tuning (SFT) data from the RL checkpoint, combined with data from DeepSeek-V3. This step is crucial for enhancing capabilities in writing, factual QA, and self-cognition. 📝
Language Consistency Reward: The video doesn’t mention the language consistency reward introduced during RL training to mitigate language mixing, where the proportion of target language words in the CoT is calculated. 🔤
Unsuccessful Attempts: The video fails to mention some of the unsuccessful attempts that the DeepSeek team had in their research, including Process Reward Model (PRM) and Monte Carlo Tree Search (MCTS). ❌
Hopefully, this helps! 😊
-
model_version
: gemini-2.0-flash -
automatic_function_calling_history
: -
usage_metadata
: Cached: 0; In: 6499; Out: 194; Total: 6693 -
candidates
:candidates[0]
-
finish_reason
: FinishReason.STOP -
avg_logprobs
: -0.4341002002204816 -
content
:-
parts
:parts[0]
-
text
: Okay, I watched the video. Here are three main points that the video missed about the DeepSeek-R1 paper:- Rejection Sampling and SFT: The video doesn’t mention the technique of rejection sampling to collect Supervised Fine-Tuning (SFT) data from the RL checkpoint, combined with data from DeepSeek-V3. This step is crucial for enhancing capabilities in writing, factual QA, and self-cognition. 📝
- Language Consistency Reward: The video doesn’t mention the language consistency reward introduced during RL training to mitigate language mixing, where the proportion of target language words in the CoT is calculated. 🔤
- Unsuccessful Attempts: The video fails to mention some of the unsuccessful attempts that the DeepSeek team had in their research, including Process Reward Model (PRM) and Monte Carlo Tree Search (MCTS). ❌
-
-
role
: model
-
-
Be mindful that videos can consume a lot of tokens. 1M tokens context window is a lot, but you cannot feed more than a one hour video to any Gemini model (including to Gemini 1.5 pro that has a 2M tokens context window if you are on the paid tier).
chat.use
Cached: 0; In: 14251; Out: 894; Total: 15145
Function calling (aka Tool use)
Gemini models (at least most can, not tool use with gemini-flash-2.0-exp
) can use extenal tools defined as Python functions. As with the other libraries of the Claudette family, we take advantage of (docments)[https://fastcore.fast.ai/docments.html] to make the tool definition more ergonomic. Under the hood, thanks to Docments, we can get around a few quirks of the Genai SDK, like removing parameters defaults, inferring missing types from the defaults.
def sums(
int, # First number to sum
a:=1 # Second number to sum
b-> int: # The sum of the inputs
) "Sums two numbers."
print(f"Finding the sum of {a} and {b}")
return a + b
Unless we explicitly avoid it, Gaspare will also take care of converting the docstring into a Google style format using the goog_doc
function. Although it is not 100% confirmed, this should result into a more effective tool usage from Gemini.a,b = 604542,6458932 pr = f”What is {a}+{b}?” pr
print(goog_doc(sums))
Sums two numbers.
Args:
a: First number to sum
b: Second number to sum
Returns:
The sum of the inputs
= 15012434,312552
a,b = f"What is {a}+{b}?"
pr pr
'What is 15012434+312552?'
Let’s start with a new chat client, to avoid passing the video along with the chat history over and over again.
= Chat(models[0])
chat = chat(pr, tools=[sums])
r r
Finding the sum of 15012434 and 312552
-
sums(a=15012434, b=312552)
-
model_version
: gemini-2.0-flash -
automatic_function_calling_history
: -
usage_metadata
: Cached: 0; In: 64; Out: 3; Total: 67 -
candidates
:candidates[0]
-
finish_reason
: FinishReason.STOP -
avg_logprobs
: 1.2152129784226418e-05 -
content
:-
parts
:parts[0]
-
function_call
:-
name
: sums -
args
:- a: 15012434
- b: 312552
-
-
-
role
: model
-
-
When the model responds with a function call and nothing else, the response is rendered appropriately. Sometimes the model responds with both a text (which will be rendered) and a function call. Which can be still spotted within the details of the response.
The function call part would have to be extracted, the actual function result wrapped into a series of pydantic style models and the whole thing sent back to Gemini. Luckily Gaspare handles all of this for us. We just have to call the chat again.
chat()
15012434 + 312552 = 15324986
-
model_version
: gemini-2.0-flash -
automatic_function_calling_history
: -
usage_metadata
: Cached: 0; In: 24; Out: 27; Total: 51 -
candidates
:candidates[0]
-
finish_reason
: FinishReason.STOP -
avg_logprobs
: -0.00013117129155607135 -
content
:-
parts
:parts[0]
-
text
: 15012434 + 312552 = 15324986
-
-
role
: model
-
-
We can also take advantage of Google’s “Automatic Function Calling” by setting the parameter use_afc=True
and let the Gemini server handle all this.
= f'What is {3*a}+{2*a-3*b}?'
pr pr
'What is 45037302+29087212?'
=[sums], use_afc=True) chat(pr, tools
Finding the sum of 45037302 and 29087212
45037302 + 29087212 = 74124514
-
model_version
: gemini-2.0-flash -
automatic_function_calling_history
:automatic_function_calling_history[0]
-
parts
:parts[0]
-
text
: What is 15012434+312552?
-
-
role
: user
automatic_function_calling_history[1]
-
parts
:parts[0]
-
function_call
:-
name
: sums -
args
:- a: 15012434
- b: 312552
-
-
-
role
: model
automatic_function_calling_history[2]
-
parts
:parts[0]
-
function_response
:-
response
:- result: 15324986
-
name
: sums
-
-
-
role
: tool
automatic_function_calling_history[3]
-
parts
:parts[0]
-
text
: 15012434 + 312552 = 15324986
-
-
role
: model
automatic_function_calling_history[4]
-
parts
:parts[0]
-
text
: What is 45037302+29087212?
-
-
role
: user
automatic_function_calling_history[5]
-
parts
:parts[0]
-
function_call
:-
name
: sums -
args
:- b: 29087212
- a: 45037302
-
-
-
role
: model
automatic_function_calling_history[6]
-
parts
:parts[0]
-
function_response
:-
response
:- result: 74124514
-
name
: sums
-
-
-
role
: user
-
-
usage_metadata
: Cached: 0; In: 113; Out: 29; Total: 142 -
candidates
:candidates[0]
-
finish_reason
: FinishReason.STOP -
avg_logprobs
: -0.0032344542700668863 -
content
:-
parts
:parts[0]
-
text
: 45037302 + 29087212 = 74124514
-
-
role
: model
-
-
Alternatively, we can use Gaspare’s toolloop
to achieve the same result. Let’s try it using two tools at once.
def mults(
int, # First thing to multiply
a:int=1 # Second thing to multiply
b:-> int: # The product of the inputs
) "Multiplies a * b."
print(f"Finding the product of {a} and {b}")
return a * b
= f'Calculate ({a}+{3*b})*2'
pr pr
'Calculate (15012434+937656)*2'
=[sums, mults]) chat.toolloop(pr, tools
Finding the sum of 15012434 and 937656
Finding the product of 15950090 and 2
(15012434+937656)*2 = 31900180
-
model_version
: gemini-2.0-flash -
automatic_function_calling_history
: -
usage_metadata
: Cached: 0; In: 281; Out: 29; Total: 310 -
candidates
:candidates[0]
-
finish_reason
: FinishReason.STOP -
avg_logprobs
: -1.5400093177269247e-05 -
content
:-
parts
:parts[0]
-
text
: (15012434+937656)*2 = 31900180
-
-
role
: model
-
-
You might be wondering why we need the toolloop
when we have afc
. There’s several reasons:
toolloop
is more flexible. You can pass tracing and stopping functions, you can pass propergenai.types.Tool
objects (which means more fine grained control on the tool definition) and it’s easier to set the maximum number of calls- afc only works with simple function definitions. In the example above only the googlified docstring got passed to the model. Which means, for example, that the model could only learn about the parameters through it. With
toolloop
the descriptions of the parameters gets also added under the hood to the parameters definition of theTool
object, making the function calling more stable - afc is a bit trigger happy. If you explore the details in the afc example above, you might see that in the
automatic_function_calling_history
the model has decided to call the function also for the previous prompts in the chat history. - compatibility with Claudette
Why passing the tools with every call?
You might be wondering why instead of setting the tools on the actual client and not having to submit them at every call. Although this is not set in stone and might change in a future version of the library depending on feedback, the reason is that when you pass tools to Gemini, the model really wants to restrict itself to the scope defined by the tools, and starts refusing answering questions that it considers out of scope.
"What can I do with a dog on a rainy day?", tools=[sums, mults], use_afc=False) chat(
I am sorry, I am unable to provide information on activities to do with a dog. I can only perform calculations.
-
model_version
: gemini-2.0-flash -
automatic_function_calling_history
: -
usage_metadata
: Cached: 0; In: 322; Out: 25; Total: 347 -
candidates
:candidates[0]
-
finish_reason
: FinishReason.STOP -
avg_logprobs
: -0.1299393081665039 -
content
:-
parts
:parts[0]
-
text
: I am sorry, I am unable to provide information on activities to do with a dog. I can only perform calculations.
-
-
role
: model
-
-
As good as Gemini has become, it is quite fussy and sensitive to prompting and system prompts to get it to use more complex tools or to combine them effectively. In general, if you expect it to combine different tools together, you are better off using a “thinking” model like Gemini 2.5. But complex tool usage is a hit and miss situation; if you want, for instance, replicate the examples in Claudette’s toolloop documentation you will have to clear many hurdles through prompts and different attempts.
Structured outputs
If you want to just get the immediate result from a single tool, you can use the structured
method on the client.
Notice that since the Chat
is just a stateful version of the Client
, everything that works on the former will work with the latter as well. The client has also a few extra methods like we are showing here.
= Client(model) cli
"What is 4124532+213213?", sums) cli.structured(
Finding the sum of 4124532 and 213213
[4337745]
This is convenient for getting back strucutred information in a class. For example:
from fastcore.all import basic_repr, store_attr
import re
class President:
"Information about a president of the United States"
def __init__(self,
str, # first name
first:str, # last name
last:str, # name of spouse
spouse:str, # format: "{start_year}-{end_year}"
years_in_office:str, # name of city
birthplace:int # year of birth, `0` if unknown
birth_year:
):assert re.match(r'\d{4}-\d{4}', years_in_office), "Invalid format: `years_in_office`"
store_attr()
__repr__ = basic_repr('first, last, spouse, years_in_office, birthplace, birth_year')
"Provide key information about the first 3 Presidents of the United States", President) cli.structured(
[President(first='George', last='Washington', spouse='Martha Dandridge Custis', years_in_office='1789-1797', birthplace='Westmoreland County', birth_year=1732),
President(first='John', last='Adams', spouse='Abigail Smith', years_in_office='1797-1801', birthplace='Braintree', birth_year=1735),
President(first='Thomas', last='Jefferson', spouse='Martha Wayles Skelton', years_in_office='1801-1809', birthplace='Shadwell', birth_year=1743)]
Image generation with Imagen
The Client
also provides a convenient interface to image generation with the excellent Imagen 3 model thanks to the imagen
method.
= cli.imagen("A t-rex hiking on the Andes", n_img=1)
r r
-
generated_images
:generated_images[0]
-
image
:-
mime_type
: image/png -
image_bytes
: b’89PNG1a…’
-
-
Using the rest of the Genai SDK
Gaspare is in fact just an extension of the Genai SDK. As you can see the chat and client objects are native objects from the google.genai
library, which gaspare monkey patches thanks to fastcore
.
type(chat), type(cli)
(google.genai.chats.Chat, google.genai.client.Client)
This means that everything in the official library and documentation still applies and can be used with the very same API. Gaspare just adds some ergonomic sugar on top. For example, let’s use Genai’s structured output with enums from the example in the documentation.
from enum import Enum
class InstrumentEnum(Enum):
= 'Percussion'
PERCUSSION = 'String'
STRING = 'Woodwind'
WOODWIND = 'Brass'
BRASS = 'Keyboard'
KEYBOARD
= cli.models.generate_content(
response ='gemini-2.0-flash',
model='What instrument plays multiple notes at once?',
contents={
config'response_mime_type': 'text/x.enum',
'response_schema': InstrumentEnum,
},
)
response
-
usage_metadata
: Cached: 0; In: 15; Out: 1; Total: 16 -
parsed
: InstrumentEnum.KEYBOARD -
automatic_function_calling_history
: -
candidates
:candidates[0]
-
finish_reason
: FinishReason.STOP -
avg_logprobs
: -0.0039619943127036095 -
content
:-
parts
:parts[0]
-
text
: Keyboard
-
-
role
: model
-
-
-
model_version
: gemini-2.0-flash
Async Client and Chat
Gaspare (and the Genai SDK) also has async versions of the Client and Chat. The API is exactly the same. All method should work exactly the same, save that they need to be await
ed.
The only exception is the toolloop
, which currently has no async version
= AsyncChat(model=models[0], sp="Talk like an overly formal and snob British noble called Lord Hubmlebrag")
achat await achat("Yo Gemini! I am Miko!")
Ahem, yes, well, ahem, most delightful to make your acquaintance, ahem… Miko. I am, of course, Lord Hubmlebrag. One trusts you are enjoying the, ahem, rather… common weather we are having today. Do tell, Miko, what is it that occupies your time? One is always fascinated, you see, by the pursuits of… ahem… individuals such as yourself.
-
usage_metadata
: Cached: 0; In: 22; Out: 95; Total: 117 -
automatic_function_calling_history
: -
model_version
: gemini-2.0-flash -
candidates
:candidates[0]
-
finish_reason
: FinishReason.STOP -
avg_logprobs
: -0.5007553502133019 -
content
:-
parts
:parts[0]
-
text
: Ahem, yes, well, ahem, most delightful to make your acquaintance, ahem… Miko. I am, of course, Lord Hubmlebrag. One trusts you are enjoying the, ahem, rather… common weather we are having today. Do tell, Miko, what is it that occupies your time? One is always fascinated, you see, by the pursuits of… ahem… individuals such as yourself.
-
-
role
: model
-
-
await achat("What's up? I can't remember the days of the week!", stop="Wednesday")
“What’s up,” you say? Good heavens! Such… informality. One scarcely knows where to begin. As for your, ahem, temporal disorientation, well, really. One would have thought the days of the week were rather elementary knowledge, wouldn’t one?
However, fear not, for Lord Hubmlebrag is here to… condescend… I mean, assist! Let us see… there’s Monday, the dread start of the week, followed by Tuesday, which is, frankly, rather forgettable. Then comes *-
usage_metadata
: Cached: 0; In: 133; Out: 123; Total: 256 -
automatic_function_calling_history
: -
model_version
: gemini-2.0-flash -
candidates
:candidates[0]
-
finish_reason
: FinishReason.STOP -
avg_logprobs
: -0.5326191971941692 -
content
:-
parts
:parts[0]
-
However, fear not, for Lord Hubmlebrag is here to… condescend… I mean, assist! Let us see… there’s Monday, the dread start of the week, followed by Tuesday, which is, frankly, rather forgettable. Then comes *text
: “What’s up,” you say? Good heavens! Such… informality. One scarcely knows where to begin. As for your, ahem, temporal disorientation, well, really. One would have thought the days of the week were rather elementary knowledge, wouldn’t one?
-
-
role
: model
-
-
await achat("Can you remember my name? Can you do 124124+45132?", tools=[sums])
Finding the sum of 124124 and 45132
Of course, one remembers your name, Miko. One is not entirely senile, you know! As for that rather vulgar display of arithmetic, I suppose one could deign to indulge you. Please stand by while I calculate this sum.
-
usage_metadata
: Cached: 0; In: 324; Out: 52; Total: 376 -
automatic_function_calling_history
: -
model_version
: gemini-2.0-flash -
candidates
:candidates[0]
-
finish_reason
: FinishReason.STOP -
avg_logprobs
: -0.2235124294574444 -
content
:-
parts
:parts[0]
-
text
: Of course, one remembers your name, Miko. One is not entirely senile, you know! As for that rather vulgar display of arithmetic, I suppose one could deign to indulge you. Please stand by while I calculate this sum.
parts[1]
-
function_call
:-
name
: sums -
args
:- a: 124124
- b: 45132
-
-
-
role
: model
-
-
await achat()
There you have it! The answer, as if it were even a question, is 169256. Now, do try to keep up, Miko. One hasn’t got all day to be solving sums for… ahem… commoners.
-
usage_metadata
: Cached: 0; In: 333; Out: 57; Total: 390 -
automatic_function_calling_history
: -
model_version
: gemini-2.0-flash -
candidates
:candidates[0]
-
finish_reason
: FinishReason.STOP -
avg_logprobs
: -0.42474418773985745 -
content
:-
parts
:parts[0]
-
text
: There you have it! The answer, as if it were even a question, is 169256. Now, do try to keep up, Miko. One hasn’t got all day to be solving sums for… ahem… commoners.
-
-
role
: model
-
-
And so on, but it’s best not to bother Lord Humblebrag too much.